Changeset 16779 for trunk/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.1.1/SimSharp-3.1.1/Core/Environment.cs
- Timestamp:
- 04/12/19 13:45:11 (5 years ago)
- Location:
- trunk/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.1.1
- Files:
-
- 1 edited
- 1 copied
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.1.1/SimSharp-3.1.1/Core/Environment.cs
r15972 r16779 1 1 #region License Information 2 2 /* SimSharp - A .NET port of SimPy, discrete event simulation framework 3 Copyright (C) 201 6Heuristic and Evolutionary Algorithms Laboratory (HEAL)3 Copyright (C) 2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) 4 4 5 5 This program is free software: you can redistribute it and/or modify … … 23 23 namespace SimSharp { 24 24 /// <summary> 25 /// Environmentshold the event queues, schedule and process events.25 /// Simulation hold the event queues, schedule and process events. 26 26 /// </summary> 27 public class Environment { 27 /// <remarks> 28 /// This class is not thread-safe against manipulation of the event queue. If you supply a termination 29 /// event that is set outside the simulation, please use the <see cref="ThreadSafeSimulation"/> environment. 30 /// 31 /// For most purposes <see cref="Simulation"/> is however the better and faster choice. 32 /// </remarks> 33 public class Simulation { 28 34 private const int InitialMaxEvents = 1024; 29 private object locker = new object();30 35 31 36 /// <summary> … … 65 70 public int ProcessedEvents { get; protected set; } 66 71 67 public Environment() : this(new DateTime(1970, 1, 1)) { } 68 public Environment(TimeSpan? defaultStep) : this(new DateTime(1970, 1, 1), defaultStep) { } 69 public Environment(int randomSeed, TimeSpan? defaultStep = null) : this(new DateTime(1970, 1, 1), randomSeed, defaultStep) { } 70 public Environment(DateTime initialDateTime, TimeSpan? defaultStep = null) { 72 public Simulation() : this(new DateTime(1970, 1, 1)) { } 73 public Simulation(TimeSpan? defaultStep) : this(new DateTime(1970, 1, 1), defaultStep) { } 74 public Simulation(int randomSeed, TimeSpan? defaultStep = null) : this(new DateTime(1970, 1, 1), randomSeed, defaultStep) { } 75 public Simulation(DateTime initialDateTime, TimeSpan? defaultStep = null) : this(new PcgRandom(), initialDateTime, defaultStep) { } 76 public Simulation(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) : this(new PcgRandom(randomSeed), initialDateTime, defaultStep) { } 77 public Simulation(IRandom random, DateTime initialDateTime, TimeSpan? defaultStep = null) { 71 78 DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds; 72 79 StartDate = initialDateTime; 73 80 Now = initialDateTime; 74 Random = new SystemRandom(); 75 ScheduleQ = new EventQueue(InitialMaxEvents); 76 Logger = Console.Out; 77 } 78 public Environment(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) { 79 DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds; 80 StartDate = initialDateTime; 81 Now = initialDateTime; 82 Random = new SystemRandom(randomSeed); 81 Random = random; 83 82 ScheduleQ = new EventQueue(InitialMaxEvents); 84 83 Logger = Console.Out; … … 127 126 ProcessedEvents = 0; 128 127 Now = StartDate; 129 Random = new SystemRandom(randomSeed);128 Random = new PcgRandom(randomSeed); 130 129 ScheduleQ = new EventQueue(InitialMaxEvents); 130 useSpareNormal = false; 131 131 } 132 132 … … 139 139 /// </summary> 140 140 /// <param name="event">The event that should be scheduled.</param> 141 /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param> </param>141 /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param> 142 142 public virtual void Schedule(Event @event, int priority = 0) { 143 lock (locker) { 144 DoSchedule(Now, @event, priority); 145 } 143 DoSchedule(Now, @event, priority); 146 144 } 147 145 … … 158 156 if (delay < TimeSpan.Zero) 159 157 throw new ArgumentException("Negative delays are not allowed in Schedule(TimeSpan, Event)."); 160 lock (locker) { 161 var eventTime = Now + delay; 162 DoSchedule(eventTime, @event, priority); 163 } 158 var eventTime = Now + delay; 159 DoSchedule(eventTime, @event, priority); 164 160 } 165 161 … … 193 189 } 194 190 191 protected bool _stopRequested = false; 192 /// <summary> 193 /// Run until a certain event is processed. 194 /// </summary> 195 /// <remarks> 196 /// This simulation environment is not thread-safe, thus triggering this event outside the environment 197 /// leads to potential race conditions. Please use the <see cref="ThreadSafeSimulation"/> environment in case you 198 /// require this functionality. Note that the performance of <see cref="ThreadSafeSimulation"/> is lower due to locking. 199 /// 200 /// For real-time based termination, you can also call <see cref="StopAsync"/> which sets a flag indicating the simulation 201 /// to stop before processing the next event. 202 /// </remarks> 203 /// <param name="stopEvent">The event that stops the simulation.</param> 204 /// <returns></returns> 195 205 public virtual object Run(Event stopEvent = null) { 206 _stopRequested = false; 196 207 if (stopEvent != null) { 197 208 if (stopEvent.IsProcessed) return stopEvent.Value; … … 200 211 201 212 try { 202 var stop = ScheduleQ.Count == 0 ;213 var stop = ScheduleQ.Count == 0 || _stopRequested; 203 214 while (!stop) { 204 215 Step(); 205 216 ProcessedEvents++; 206 lock (locker) { 207 stop = ScheduleQ.Count == 0; 217 stop = ScheduleQ.Count == 0 || _stopRequested; 218 } 219 } catch (StopSimulationException e) { return e.Value; } 220 if (stopEvent == null) return null; 221 if (!stopEvent.IsTriggered) throw new InvalidOperationException("No scheduled events left but \"until\" event was not triggered."); 222 return stopEvent.Value; 223 } 224 225 public virtual void StopAsync() { 226 _stopRequested = true; 227 } 228 229 /// <summary> 230 /// Performs a single step of the simulation, i.e. process a single event 231 /// </summary> 232 /// <remarks> 233 /// This method is not thread-safe 234 /// </remarks> 235 public virtual void Step() { 236 Event evt; 237 var next = ScheduleQ.Dequeue(); 238 Now = next.PrimaryPriority; 239 evt = next.Event; 240 evt.Process(); 241 } 242 243 /// <summary> 244 /// Peeks at the time of the next event in terms of the defined step 245 /// </summary> 246 /// <remarks> 247 /// This method is not thread-safe 248 /// </remarks> 249 public virtual double PeekD() { 250 if (ScheduleQ.Count == 0) return double.MaxValue; 251 return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds; 252 } 253 254 /// <summary> 255 /// Peeks at the time of the next event 256 /// </summary> 257 /// <remarks> 258 /// This method is not thread-safe 259 /// </remarks> 260 public virtual DateTime Peek() { 261 return ScheduleQ.Count > 0 ? ScheduleQ.First.PrimaryPriority : DateTime.MaxValue; 262 } 263 264 protected virtual void StopSimulation(Event @event) { 265 throw new StopSimulationException(@event.Value); 266 } 267 268 public virtual void Log(string message, params object[] args) { 269 if (Logger != null) 270 Logger.WriteLine(message, args); 271 } 272 273 #region Random number distributions 274 public double RandUniform(IRandom random, double a, double b) { 275 return a + (b - a) * random.NextDouble(); 276 } 277 public double RandUniform(double a, double b) { 278 return RandUniform(Random, a, b); 279 } 280 281 public TimeSpan RandUniform(IRandom random, TimeSpan a, TimeSpan b) { 282 return TimeSpan.FromSeconds(RandUniform(random, a.TotalSeconds, b.TotalSeconds)); 283 } 284 public TimeSpan RandUniform(TimeSpan a, TimeSpan b) { 285 return RandUniform(Random, a, b); 286 } 287 public double RandTriangular(IRandom random, double low, double high) { 288 var u = random.NextDouble(); 289 if (u > 0.5) 290 return high + (low - high) * Math.Sqrt(((1.0 - u) / 2)); 291 return low + (high - low) * Math.Sqrt(u / 2); 292 } 293 public double RandTriangular(double low, double high) { 294 return RandTriangular(Random, low, high); 295 } 296 297 public TimeSpan RandTriangular(IRandom random, TimeSpan low, TimeSpan high) { 298 return TimeSpan.FromSeconds(RandTriangular(random, low.TotalSeconds, high.TotalSeconds)); 299 } 300 public TimeSpan RandTriangular(TimeSpan low, TimeSpan high) { 301 return RandTriangular(Random, low, high); 302 } 303 304 public double RandTriangular(IRandom random, double low, double high, double mode) { 305 var u = random.NextDouble(); 306 var c = (mode - low) / (high - low); 307 if (u > c) 308 return high + (low - high) * Math.Sqrt(((1.0 - u) * (1.0 - c))); 309 return low + (high - low) * Math.Sqrt(u * c); 310 } 311 public double RandTriangular(double low, double high, double mode) { 312 return RandTriangular(Random, low, high, mode); 313 } 314 315 public TimeSpan RandTriangular(IRandom random, TimeSpan low, TimeSpan high, TimeSpan mode) { 316 return TimeSpan.FromSeconds(RandTriangular(random, low.TotalSeconds, high.TotalSeconds, mode.TotalSeconds)); 317 } 318 public TimeSpan RandTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) { 319 return RandTriangular(Random, low, high, mode); 320 } 321 322 /// <summary> 323 /// Returns a number that is exponentially distributed given a certain mean. 324 /// </summary> 325 /// <remarks> 326 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 327 /// </remarks> 328 /// <param name="random">The random number generator to use.</param> 329 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 330 /// <returns>A number that is exponentially distributed</returns> 331 public double RandExponential(IRandom random, double mean) { 332 return -Math.Log(1 - random.NextDouble()) * mean; 333 } 334 /// <summary> 335 /// Returns a number that is exponentially distributed given a certain mean. 336 /// </summary> 337 /// <remarks> 338 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 339 /// </remarks> 340 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 341 /// <returns>A number that is exponentially distributed</returns> 342 public double RandExponential(double mean) { 343 return RandExponential(Random, mean); 344 } 345 346 /// <summary> 347 /// Returns a timespan that is exponentially distributed given a certain mean. 348 /// </summary> 349 /// <remarks> 350 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 351 /// </remarks> 352 /// <param name="random">The random number generator to use.</param> 353 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 354 /// <returns>A number that is exponentially distributed</returns> 355 public TimeSpan RandExponential(IRandom random, TimeSpan mean) { 356 return TimeSpan.FromSeconds(RandExponential(random, mean.TotalSeconds)); 357 } 358 /// <summary> 359 /// Returns a timespan that is exponentially distributed given a certain mean. 360 /// </summary> 361 /// <remarks> 362 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 363 /// </remarks> 364 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 365 /// <returns>A number that is exponentially distributed</returns> 366 public TimeSpan RandExponential(TimeSpan mean) { 367 return RandExponential(Random, mean); 368 } 369 370 private bool useSpareNormal = false; 371 private double spareNormal = double.NaN; 372 /// <summary> 373 /// Uses the Marsaglia polar method to generate a random variable 374 /// from two uniform random distributed values. 375 /// </summary> 376 /// <remarks> 377 /// A spare random variable is generated from the second uniformly 378 /// distributed value. Thus, the two calls to the uniform random number 379 /// generator will be made only every second call. 380 /// </remarks> 381 /// <param name="random">The random number generator to use.</param> 382 /// <param name="mu">The mean of the normal distribution.</param> 383 /// <param name="sigma">The standard deviation of the normal distribution.</param> 384 /// <returns>A number that is normal distributed.</returns> 385 public virtual double RandNormal(IRandom random, double mu, double sigma) { 386 if (useSpareNormal) { 387 useSpareNormal = false; 388 return spareNormal * sigma + mu; 389 } else { 390 double u, v, s; 391 do { 392 u = random.NextDouble() * 2 - 1; 393 v = random.NextDouble() * 2 - 1; 394 s = u * u + v * v; 395 } while (s >= 1 || s == 0); 396 var mul = Math.Sqrt(-2.0 * Math.Log(s) / s); 397 spareNormal = v * mul; 398 useSpareNormal = true; 399 return mu + sigma * u * mul; 400 } 401 } 402 /// <summary> 403 /// Uses the Marsaglia polar method to generate a random variable 404 /// from two uniform random distributed values. 405 /// </summary> 406 /// <remarks> 407 /// A spare random variable is generated from the second uniformly 408 /// distributed value. Thus, the two calls to the uniform random number 409 /// generator will be made only every second call. 410 /// </remarks> 411 /// <param name="mu">The mean of the normal distribution.</param> 412 /// <param name="sigma">The standard deviation of the normal distribution.</param> 413 /// <returns>A number that is normal distributed.</returns> 414 public double RandNormal(double mu, double sigma) { 415 return RandNormal(Random, mu, sigma); 416 } 417 418 /// <summary> 419 /// Uses the Marsaglia polar method to generate a random variable 420 /// from two uniform random distributed values. 421 /// </summary> 422 /// <remarks> 423 /// A spare random variable is generated from the second uniformly 424 /// distributed value. Thus, the two calls to the uniform random number 425 /// generator will be made only every second call. 426 /// </remarks> 427 /// <param name="random">The random number generator to use.</param> 428 /// <param name="mu">The mean of the normal distribution.</param> 429 /// <param name="sigma">The standard deviation of the normal distribution.</param> 430 /// <returns>A number that is normal distributed.</returns> 431 public TimeSpan RandNormal(IRandom random, TimeSpan mu, TimeSpan sigma) { 432 return TimeSpan.FromSeconds(RandNormal(random, mu.TotalSeconds, sigma.TotalSeconds)); 433 } 434 /// <summary> 435 /// Uses the Marsaglia polar method to generate a random variable 436 /// from two uniform random distributed values. 437 /// </summary> 438 /// <remarks> 439 /// A spare random variable is generated from the second uniformly 440 /// distributed value. Thus, the two calls to the uniform random number 441 /// generator will be made only every second call. 442 /// </remarks> 443 /// <param name="mu">The mean of the normal distribution.</param> 444 /// <param name="sigma">The standard deviation of the normal distribution.</param> 445 /// <returns>A number that is normal distributed.</returns> 446 public TimeSpan RandNormal(TimeSpan mu, TimeSpan sigma) { 447 return RandNormal(Random, mu, sigma); 448 } 449 450 public double RandNormalPositive(IRandom random, double mu, double sigma) { 451 double val; 452 do { 453 val = RandNormal(random, mu, sigma); 454 } while (val <= 0); 455 return val; 456 } 457 public double RandNormalPositive(double mu, double sigma) { 458 return RandNormalPositive(Random, mu, sigma); 459 } 460 461 public TimeSpan RandNormalPositive(IRandom random, TimeSpan mu, TimeSpan sigma) { 462 return TimeSpan.FromSeconds(RandNormalPositive(random, mu.TotalSeconds, sigma.TotalSeconds)); 463 } 464 public TimeSpan RandNormalPositive(TimeSpan mu, TimeSpan sigma) { 465 return RandNormalPositive(Random, mu, sigma); 466 } 467 468 public double RandNormalNegative(IRandom random, double mu, double sigma) { 469 double val; 470 do { 471 val = RandNormal(random, mu, sigma); 472 } while (val >= 0); 473 return val; 474 } 475 public double RandNormalNegative(double mu, double sigma) { 476 return RandNormalNegative(Random, mu, sigma); 477 } 478 479 public TimeSpan RandNormalNegative(IRandom random, TimeSpan mu, TimeSpan sigma) { 480 return TimeSpan.FromSeconds(RandNormalNegative(random, mu.TotalSeconds, sigma.TotalSeconds)); 481 } 482 public TimeSpan RandNormalNegative(TimeSpan mu, TimeSpan sigma) { 483 return RandNormalNegative(Random, mu, sigma); 484 } 485 486 /// <summary> 487 /// Returns values from a log-normal distribution with the mean 488 /// exp(mu + sigma^2 / 2) 489 /// and the standard deviation 490 /// sqrt([exp(sigma^2)-1] * exp(2 * mu + sigma^2)) 491 /// </summary> 492 /// <param name="random">The random number generator to use.</param> 493 /// <param name="mu">The mu parameter of the log-normal distribution (not the mean).</param> 494 /// <param name="sigma">The sigma parameter of the log-normal distribution (not the standard deviation).</param> 495 /// <returns>A log-normal distributed random value.</returns> 496 public double RandLogNormal(IRandom random, double mu, double sigma) { 497 return Math.Exp(RandNormal(random, mu, sigma)); 498 } 499 /// <summary> 500 /// Returns values from a log-normal distribution with the mean 501 /// exp(mu + sigma^2 / 2) 502 /// and the standard deviation 503 /// sqrt([exp(sigma^2)-1] * exp(2 * mu + sigma^2)) 504 /// </summary> 505 /// <param name="mu">The mu parameter of the log-normal distribution (not the mean).</param> 506 /// <param name="sigma">The sigma parameter of the log-normal distribution (not the standard deviation).</param> 507 /// <returns>A log-normal distributed random value.</returns> 508 public double RandLogNormal(double mu, double sigma) { 509 return RandLogNormal(Random, mu, sigma); 510 } 511 512 /// <summary> 513 /// Returns values from a log-normal distribution with 514 /// the mean <paramref name="mean"/> and standard deviation <paramref name="stdev"/>. 515 /// </summary> 516 /// <param name="random">The random number generator to use.</param> 517 /// <param name="mean">The distribution mean.</param> 518 /// <param name="stdev">The distribution standard deviation.</param> 519 /// <returns>A log-normal distributed random value.</returns> 520 public double RandLogNormal2(IRandom random, double mean, double stdev) { 521 if (stdev == 0) return mean; 522 var alpha = Math.Sqrt(mean * stdev) / mean; 523 var sigma = Math.Sqrt(Math.Log(1 + (alpha * alpha))); 524 var mu = Math.Log(mean) - 0.5 * sigma * sigma; 525 return Math.Exp(RandNormal(random, mu, sigma)); 526 } 527 /// <summary> 528 /// Returns values from a log-normal distribution with 529 /// the mean <paramref name="mean"/> and standard deviation <paramref name="stdev"/>. 530 /// </summary> 531 /// <param name="mean">The distribution mean.</param> 532 /// <param name="stdev">The distribution standard deviation.</param> 533 /// <returns>A log-normal distributed random value.</returns> 534 public double RandLogNormal2(double mean, double stdev) { 535 return RandLogNormal2(Random, mean, stdev); 536 } 537 538 /// <summary> 539 /// Returns a timespan value from a log-normal distribution with the mean 540 /// exp(mu + sigma^2 / 2) 541 /// and the standard deviation 542 /// sqrt([exp(sigma^2)-1] * exp(2 * mu + sigma^2)) 543 /// </summary> 544 /// <param name="random">The random number generator to use.</param> 545 /// <param name="mu">The mu parameter of the log-normal distribution (not the mean).</param> 546 /// <param name="sigma">The sigma parameter of the log-normal distribution (not the standard deviation).</param> 547 /// <returns>A log-normal distributed random timespan.</returns> 548 public TimeSpan RandLogNormal(IRandom random, TimeSpan mu, TimeSpan sigma) { 549 return TimeSpan.FromSeconds(RandLogNormal(random, mu.TotalSeconds, sigma.TotalSeconds)); 550 } 551 /// <summary> 552 /// Returns a timespan value from a log-normal distribution with the mean 553 /// exp(mu + sigma^2 / 2) 554 /// and the standard deviation 555 /// sqrt([exp(sigma^2)-1] * exp(2 * mu + sigma^2)) 556 /// </summary> 557 /// <param name="mu">The mu parameter of the log-normal distribution (not the mean).</param> 558 /// <param name="sigma">The sigma parameter of the log-normal distribution (not the standard deviation).</param> 559 /// <returns>A log-normal distributed random timespan.</returns> 560 public TimeSpan RandLogNormal(TimeSpan mu, TimeSpan sigma) { 561 return RandLogNormal(Random, mu, sigma); 562 } 563 564 /// <summary> 565 /// Returns a timespan value from a log-normal distribution with 566 /// the mean <paramref name="mean"/> and standard deviation <paramref name="stdev"/>. 567 /// </summary> 568 /// <param name="random">The random number generator to use.</param> 569 /// <param name="mean">The distribution mean.</param> 570 /// <param name="stdev">The distribution standard deviation.</param> 571 /// <returns>A log-normal distributed random timespan.</returns> 572 public TimeSpan RandLogNormal2(IRandom random, TimeSpan mean, TimeSpan stdev) { 573 return TimeSpan.FromSeconds(RandLogNormal2(random, mean.TotalSeconds, stdev.TotalSeconds)); 574 } 575 /// <summary> 576 /// Returns a timespan value from a log-normal distribution with 577 /// the mean <paramref name="mean"/> and standard deviation <paramref name="stdev"/>. 578 /// </summary> 579 /// <param name="mean">The distribution mean.</param> 580 /// <param name="stdev">The distribution standard deviation.</param> 581 /// <returns>A log-normal distributed random timespan.</returns> 582 public TimeSpan RandLogNormal2(TimeSpan mean, TimeSpan stdev) { 583 return RandLogNormal2(Random, mean, stdev); 584 } 585 586 public double RandCauchy(IRandom random, double x0, double gamma) { 587 return x0 + gamma * Math.Tan(Math.PI * (random.NextDouble() - 0.5)); 588 } 589 public double RandCauchy(double x0, double gamma) { 590 return RandCauchy(Random, x0, gamma); 591 } 592 593 public TimeSpan RandCauchy(IRandom random, TimeSpan x0, TimeSpan gamma) { 594 return TimeSpan.FromSeconds(RandCauchy(random, x0.TotalSeconds, gamma.TotalSeconds)); 595 } 596 public TimeSpan RandCauchy(TimeSpan x0, TimeSpan gamma) { 597 return RandCauchy(Random, x0, gamma); 598 } 599 600 public double RandWeibull(IRandom random, double alpha, double beta) { 601 return alpha * Math.Pow(-Math.Log(1 - random.NextDouble()), 1 / beta); 602 } 603 public double RandWeibull(double alpha, double beta) { 604 return RandWeibull(Random, alpha, beta); 605 } 606 607 public TimeSpan RandWeibull(IRandom random, TimeSpan alpha, TimeSpan beta) { 608 return TimeSpan.FromSeconds(RandWeibull(random, alpha.TotalSeconds, beta.TotalSeconds)); 609 } 610 public TimeSpan RandWeibull(TimeSpan alpha, TimeSpan beta) { 611 return RandWeibull(Random, alpha, beta); 612 } 613 #endregion 614 615 #region Random timeouts 616 public Timeout TimeoutUniformD(IRandom random, double a, double b) { 617 return new Timeout(this, ToTimeSpan(RandUniform(random, a, b))); 618 } 619 public Timeout TimeoutUniformD(double a, double b) { 620 return TimeoutUniformD(Random, a, b); 621 } 622 623 public Timeout TimeoutUniform(IRandom random, TimeSpan a, TimeSpan b) { 624 return new Timeout(this, RandUniform(random, a, b)); 625 } 626 public Timeout TimeoutUniform(TimeSpan a, TimeSpan b) { 627 return TimeoutUniform(Random, a, b); 628 } 629 630 public Timeout TimeoutTriangularD(IRandom random, double low, double high) { 631 return new Timeout(this, ToTimeSpan(RandTriangular(random, low, high))); 632 } 633 public Timeout TimeoutTriangularD(double low, double high) { 634 return TimeoutTriangularD(Random, low, high); 635 } 636 637 public Timeout TimeoutTriangular(IRandom random, TimeSpan low, TimeSpan high) { 638 return new Timeout(this, RandTriangular(random, low, high)); 639 } 640 public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high) { 641 return TimeoutTriangular(Random, low, high); 642 } 643 644 public Timeout TimeoutTriangularD(IRandom random, double low, double high, double mode) { 645 return new Timeout(this, ToTimeSpan(RandTriangular(random, low, high, mode))); 646 } 647 public Timeout TimeoutTriangularD(double low, double high, double mode) { 648 return TimeoutTriangularD(Random, low, high, mode); 649 } 650 651 public Timeout TimeoutTriangular(IRandom random, TimeSpan low, TimeSpan high, TimeSpan mode) { 652 return new Timeout(this, RandTriangular(random, low, high, mode)); 653 } 654 public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) { 655 return TimeoutTriangular(Random, low, high, mode); 656 } 657 658 public Timeout TimeoutExponentialD(IRandom random, double mean) { 659 return new Timeout(this, ToTimeSpan(RandExponential(random, mean))); 660 } 661 public Timeout TimeoutExponentialD(double mean) { 662 return TimeoutExponentialD(Random, mean); 663 } 664 665 public Timeout TimeoutExponential(IRandom random, TimeSpan mean) { 666 return new Timeout(this, RandExponential(random, mean)); 667 } 668 public Timeout TimeoutExponential(TimeSpan mean) { 669 return TimeoutExponential(Random, mean); 670 } 671 672 public Timeout TimeoutNormalPositiveD(IRandom random, double mu, double sigma) { 673 return new Timeout(this, ToTimeSpan(RandNormalPositive(random, mu, sigma))); 674 } 675 public Timeout TimeoutNormalPositiveD(double mu, double sigma) { 676 return TimeoutNormalPositiveD(Random, mu, sigma); 677 } 678 679 public Timeout TimeoutNormalPositive(IRandom random, TimeSpan mu, TimeSpan sigma) { 680 return new Timeout(this, RandNormalPositive(random, mu, sigma)); 681 } 682 public Timeout TimeoutNormalPositive(TimeSpan mu, TimeSpan sigma) { 683 return TimeoutNormalPositive(Random, mu, sigma); 684 } 685 686 public Timeout TimeoutLogNormalD(IRandom random, double mu, double sigma) { 687 return new Timeout(this, ToTimeSpan(RandLogNormal(random, mu, sigma))); 688 } 689 public Timeout TimeoutLogNormalD(double mu, double sigma) { 690 return TimeoutLogNormalD(Random, mu, sigma); 691 } 692 693 public Timeout TimeoutLogNormal2D(IRandom random, double mean, double stdev) { 694 return new Timeout(this, ToTimeSpan(RandLogNormal2(random, mean, stdev))); 695 } 696 public Timeout TimeoutLogNormal2D(double mean, double stdev) { 697 return TimeoutLogNormal2D(Random, mean, stdev); 698 } 699 700 public Timeout TimeoutLogNormal(IRandom random, TimeSpan mu, TimeSpan sigma) { 701 return new Timeout(this, RandLogNormal(random, mu, sigma)); 702 } 703 public Timeout TimeoutLogNormal(TimeSpan mu, TimeSpan sigma) { 704 return TimeoutLogNormal(Random, mu, sigma); 705 } 706 707 public Timeout TimeoutLogNormal2(IRandom random, TimeSpan mean, TimeSpan stdev) { 708 return new Timeout(this, RandLogNormal2(random, mean, stdev)); 709 } 710 public Timeout TimeoutLogNormal2(TimeSpan mean, TimeSpan stdev) { 711 return TimeoutLogNormal2(Random, mean, stdev); 712 } 713 #endregion 714 } 715 716 /// <summary> 717 /// Provides a simulation environment that is thread-safe against manipulations of the event queue. 718 /// Its performance is somewhat lower than the non-thread-safe environment (cf. <see cref="Simulation"/>) 719 /// due to the locking involved. 720 /// </summary> 721 /// <remarks> 722 /// Please carefully consider if you must really schedule the stop event in a separate thread. You can also 723 /// call <see cref="Simulation.StopAsync"/> to request termination after the current event has been processed. 724 /// 725 /// The simulation will still run in only one thread and execute all events sequentially. 726 /// </remarks> 727 public class ThreadSafeSimulation : Simulation { 728 protected object _locker; 729 730 public ThreadSafeSimulation() : this(new DateTime(1970, 1, 1)) { } 731 public ThreadSafeSimulation(TimeSpan? defaultStep) : this(new DateTime(1970, 1, 1), defaultStep) { } 732 public ThreadSafeSimulation(DateTime initialDateTime, TimeSpan? defaultStep = null) : this(new PcgRandom(), initialDateTime, defaultStep) { } 733 public ThreadSafeSimulation(int randomSeed, TimeSpan? defaultStep = null) : this(new DateTime(1970, 1, 1), randomSeed, defaultStep) { } 734 public ThreadSafeSimulation(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) : this(new PcgRandom(randomSeed), initialDateTime, defaultStep) { } 735 public ThreadSafeSimulation(IRandom random, DateTime initialDateTime, TimeSpan? defaultStep = null) : base(random, initialDateTime, defaultStep) { 736 _locker = new object(); 737 } 738 739 740 /// <summary> 741 /// Schedules an event to occur at the same simulation time as the call was made. 742 /// </summary> 743 /// <remarks> 744 /// This method is thread-safe against manipulations of the event queue 745 /// </remarks> 746 /// <param name="event">The event that should be scheduled.</param> 747 /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param> 748 public override void Schedule(Event @event, int priority = 0) { 749 lock (_locker) { 750 DoSchedule(Now, @event, priority); 751 } 752 } 753 754 /// <summary> 755 /// Schedules an event to occur after a certain (positive) delay. 756 /// </summary> 757 /// <remarks> 758 /// This method is thread-safe against manipulations of the event queue 759 /// </remarks> 760 /// <exception cref="ArgumentException"> 761 /// Thrown when <paramref name="delay"/> is negative. 762 /// </exception> 763 /// <param name="delay">The (positive) delay after which the event should be fired.</param> 764 /// <param name="event">The event that should be scheduled.</param> 765 /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param> 766 public override void Schedule(TimeSpan delay, Event @event, int priority = 0) { 767 if (delay < TimeSpan.Zero) 768 throw new ArgumentException("Negative delays are not allowed in Schedule(TimeSpan, Event)."); 769 lock (_locker) { 770 var eventTime = Now + delay; 771 DoSchedule(eventTime, @event, priority); 772 } 773 } 774 775 /// <summary> 776 /// Run until a certain event is processed. 777 /// </summary> 778 /// <remarks> 779 /// This method is thread-safe against manipulations of the event queue 780 /// </remarks> 781 /// <param name="stopEvent">The event that stops the simulation.</param> 782 /// <returns></returns> 783 public override object Run(Event stopEvent = null) { 784 _stopRequested = false; 785 if (stopEvent != null) { 786 if (stopEvent.IsProcessed) return stopEvent.Value; 787 stopEvent.AddCallback(StopSimulation); 788 } 789 790 try { 791 var stop = false; 792 lock (_locker) { 793 stop = ScheduleQ.Count == 0 || _stopRequested; 794 } 795 while (!stop) { 796 Step(); 797 ProcessedEvents++; 798 lock (_locker) { 799 stop = ScheduleQ.Count == 0 || _stopRequested; 208 800 } 209 801 } … … 214 806 } 215 807 216 public virtual void Step() { 808 /// <summary> 809 /// Performs a single step of the simulation, i.e. process a single event 810 /// </summary> 811 /// <remarks> 812 /// This method is thread-safe against manipulations of the event queue 813 /// </remarks> 814 public override void Step() { 217 815 Event evt; 218 lock ( locker) {816 lock (_locker) { 219 817 var next = ScheduleQ.Dequeue(); 220 818 Now = next.PrimaryPriority; … … 224 822 } 225 823 226 public virtual double PeekD() { 227 lock (locker) { 824 /// <summary> 825 /// Peeks at the time of the next event in terms of the defined step 826 /// </summary> 827 /// <remarks> 828 /// This method is thread-safe against manipulations of the event queue 829 /// </remarks> 830 public override double PeekD() { 831 lock (_locker) { 228 832 if (ScheduleQ.Count == 0) return double.MaxValue; 229 833 return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds; … … 231 835 } 232 836 233 public virtual DateTime Peek() { 234 lock (locker) { 837 /// <summary> 838 /// Peeks at the time of the next event 839 /// </summary> 840 /// <remarks> 841 /// This method is thread-safe against manipulations of the event queue 842 /// </remarks> 843 public override DateTime Peek() { 844 lock (_locker) { 235 845 return ScheduleQ.Count > 0 ? ScheduleQ.First.PrimaryPriority : DateTime.MaxValue; 236 846 } 237 847 } 238 239 protected virtual void StopSimulation(Event @event) { 240 throw new StopSimulationException(@event.Value); 241 } 242 243 public virtual void Log(string message, params object[] args) { 244 if (Logger != null) 245 Logger.WriteLine(message, args); 246 } 247 248 #region Random number distributions 848 } 849 850 /// <summary> 851 /// Environments hold the event queues, schedule and process events. 852 /// </summary> 853 [Obsolete("Use class Simulation or ThreadSafeSimulation instead. Due to name clashes with System.Environment the class SimSharp.Environment is being outphased.")] 854 public class Environment : ThreadSafeSimulation { 855 public Environment() 856 : base() { 857 Random = new SystemRandom(); 858 } 859 public Environment(TimeSpan? defaultStep) 860 : base(defaultStep) { 861 Random = new SystemRandom(); 862 } 863 public Environment(int randomSeed, TimeSpan? defaultStep = null) 864 : base(randomSeed, defaultStep) { 865 Random = new SystemRandom(randomSeed); 866 } 867 public Environment(DateTime initialDateTime, TimeSpan? defaultStep = null) 868 : base(initialDateTime, defaultStep) { 869 Random = new SystemRandom(); 870 } 871 public Environment(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) 872 : base(initialDateTime, randomSeed, defaultStep) { 873 Random = new SystemRandom(randomSeed); 874 } 875 249 876 protected static readonly double NormalMagicConst = 4 * Math.Exp(-0.5) / Math.Sqrt(2.0); 250 251 public double RandUniform(double a, double b) { 252 return a + (b - a) * Random.NextDouble(); 253 } 254 255 public TimeSpan RandUniform(TimeSpan a, TimeSpan b) { 256 return TimeSpan.FromSeconds(RandUniform(a.TotalSeconds, b.TotalSeconds)); 257 } 258 259 public double RandTriangular(double low, double high) { 260 var u = Random.NextDouble(); 261 if (u > 0.5) 262 return high + (low - high) * Math.Sqrt(((1.0 - u) / 2)); 263 return low + (high - low) * Math.Sqrt(u / 2); 264 } 265 266 public TimeSpan RandTriangular(TimeSpan low, TimeSpan high) { 267 return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds)); 268 } 269 270 public double RandTriangular(double low, double high, double mode) { 271 var u = Random.NextDouble(); 272 var c = (mode - low) / (high - low); 273 if (u > c) 274 return high + (low - high) * Math.Sqrt(((1.0 - u) * (1.0 - c))); 275 return low + (high - low) * Math.Sqrt(u * c); 276 } 277 278 public TimeSpan RandTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) { 279 return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds, mode.TotalSeconds)); 280 } 281 282 /// <summary> 283 /// Returns a number that is exponentially distributed given a certain mean. 284 /// </summary> 285 /// <remarks> 286 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 287 /// </remarks> 288 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 289 /// <returns>A number that is exponentially distributed</returns> 290 public double RandExponential(double mean) { 291 return -Math.Log(1 - Random.NextDouble()) * mean; 292 } 293 294 /// <summary> 295 /// Returns a timespan that is exponentially distributed given a certain mean. 296 /// </summary> 297 /// <remarks> 298 /// Unlike in other APIs here the mean should be given and not the lambda parameter. 299 /// </remarks> 300 /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param> 301 /// <returns>A number that is exponentially distributed</returns> 302 public TimeSpan RandExponential(TimeSpan mean) { 303 return TimeSpan.FromSeconds(RandExponential(mean.TotalSeconds)); 304 } 305 306 public double RandNormal(double mu, double sigma) { 877 public override double RandNormal(IRandom random, double mu, double sigma) { 307 878 double z, zz, u1, u2; 308 879 do { 309 u1 = Random.NextDouble();310 u2 = 1 - Random.NextDouble();880 u1 = random.NextDouble(); 881 u2 = 1 - random.NextDouble(); 311 882 z = NormalMagicConst * (u1 - 0.5) / u2; 312 883 zz = z * z / 4.0; … … 314 885 return mu + z * sigma; 315 886 } 316 317 public TimeSpan RandNormal(TimeSpan mu, TimeSpan sigma) {318 return TimeSpan.FromSeconds(RandNormal(mu.TotalSeconds, sigma.TotalSeconds));319 }320 321 public double RandNormalPositive(double mu, double sigma) {322 double val;323 do {324 val = RandNormal(mu, sigma);325 } while (val <= 0);326 return val;327 }328 329 public TimeSpan RandNormalPositive(TimeSpan mu, TimeSpan sigma) {330 return TimeSpan.FromSeconds(RandNormalPositive(mu.TotalSeconds, sigma.TotalSeconds));331 }332 333 public double RandNormalNegative(double mu, double sigma) {334 double val;335 do {336 val = RandNormal(mu, sigma);337 } while (val >= 0);338 return val;339 }340 341 public TimeSpan RandNormalNegative(TimeSpan mu, TimeSpan sigma) {342 return TimeSpan.FromSeconds(RandNormalNegative(mu.TotalSeconds, sigma.TotalSeconds));343 }344 345 public double RandLogNormal(double mu, double sigma) {346 return Math.Exp(RandNormal(mu, sigma));347 }348 349 public TimeSpan RandLogNormal(TimeSpan mu, TimeSpan sigma) {350 return TimeSpan.FromSeconds(RandLogNormal(mu.TotalSeconds, sigma.TotalSeconds));351 }352 353 public double RandCauchy(double x0, double gamma) {354 return x0 + gamma * Math.Tan(Math.PI * (Random.NextDouble() - 0.5));355 }356 357 public TimeSpan RandCauchy(TimeSpan x0, TimeSpan gamma) {358 return TimeSpan.FromSeconds(RandCauchy(x0.TotalSeconds, gamma.TotalSeconds));359 }360 361 public double RandWeibull(double alpha, double beta) {362 return alpha * Math.Pow(-Math.Log(1 - Random.NextDouble()), 1 / beta);363 }364 365 public TimeSpan RandWeibull(TimeSpan mu, TimeSpan sigma) {366 return TimeSpan.FromSeconds(RandWeibull(mu.TotalSeconds, sigma.TotalSeconds));367 }368 #endregion369 370 #region Random timeouts371 public Timeout TimeoutUniformD(double a, double b) {372 return new Timeout(this, ToTimeSpan(RandUniform(a, b)));373 }374 375 public Timeout TimeoutUniform(TimeSpan a, TimeSpan b) {376 return new Timeout(this, RandUniform(a, b));377 }378 379 public Timeout TimeoutTriangularD(double low, double high) {380 return new Timeout(this, ToTimeSpan(RandTriangular(low, high)));381 }382 383 public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high) {384 return new Timeout(this, RandTriangular(low, high));385 }386 387 public Timeout TimeoutTriangularD(double low, double high, double mode) {388 return new Timeout(this, ToTimeSpan(RandTriangular(low, high, mode)));389 }390 391 public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {392 return new Timeout(this, RandTriangular(low, high, mode));393 }394 395 public Timeout TimeoutExponentialD(double mean) {396 return new Timeout(this, ToTimeSpan(RandExponential(mean)));397 }398 399 public Timeout TimeoutExponential(TimeSpan mean) {400 return new Timeout(this, RandExponential(mean));401 }402 403 public Timeout TimeoutNormalPositiveD(double mu, double sigma) {404 return new Timeout(this, ToTimeSpan(RandNormalPositive(mu, sigma)));405 }406 407 public Timeout TimeoutNormalPositive(TimeSpan mu, TimeSpan sigma) {408 return new Timeout(this, RandNormalPositive(mu, sigma));409 }410 411 public Timeout TimeoutLogNormalD(double mu, double sigma) {412 return new Timeout(this, ToTimeSpan(RandLogNormal(mu, sigma)));413 }414 415 public Timeout TimeoutLogNormal(TimeSpan mu, TimeSpan sigma) {416 return new Timeout(this, RandLogNormal(mu, sigma));417 }418 #endregion419 887 } 420 888 }
Note: See TracChangeset
for help on using the changeset viewer.