Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
04/12/19 13:45:11 (5 years ago)
Author:
abeham
Message:

#2975: Updated Sim# to 3.1.1

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  
    11#region License Information
    22/* SimSharp - A .NET port of SimPy, discrete event simulation framework
    3 Copyright (C) 2016  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
     3Copyright (C) 2019  Heuristic and Evolutionary Algorithms Laboratory (HEAL)
    44
    55This program is free software: you can redistribute it and/or modify
     
    2323namespace SimSharp {
    2424  /// <summary>
    25   /// Environments hold the event queues, schedule and process events.
     25  /// Simulation hold the event queues, schedule and process events.
    2626  /// </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 {
    2834    private const int InitialMaxEvents = 1024;
    29     private object locker = new object();
    3035
    3136    /// <summary>
     
    6570    public int ProcessedEvents { get; protected set; }
    6671
    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) {
    7178      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
    7279      StartDate = initialDateTime;
    7380      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;
    8382      ScheduleQ = new EventQueue(InitialMaxEvents);
    8483      Logger = Console.Out;
     
    127126      ProcessedEvents = 0;
    128127      Now = StartDate;
    129       Random = new SystemRandom(randomSeed);
     128      Random = new PcgRandom(randomSeed);
    130129      ScheduleQ = new EventQueue(InitialMaxEvents);
     130      useSpareNormal = false;
    131131    }
    132132
     
    139139    /// </summary>
    140140    /// <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>
    142142    public virtual void Schedule(Event @event, int priority = 0) {
    143       lock (locker) {
    144         DoSchedule(Now, @event, priority);
    145       }
     143      DoSchedule(Now, @event, priority);
    146144    }
    147145
     
    158156      if (delay < TimeSpan.Zero)
    159157        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);
    164160    }
    165161
     
    193189    }
    194190
     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>
    195205    public virtual object Run(Event stopEvent = null) {
     206      _stopRequested = false;
    196207      if (stopEvent != null) {
    197208        if (stopEvent.IsProcessed) return stopEvent.Value;
     
    200211
    201212      try {
    202         var stop = ScheduleQ.Count == 0;
     213        var stop = ScheduleQ.Count == 0 || _stopRequested;
    203214        while (!stop) {
    204215          Step();
    205216          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;
    208800          }
    209801        }
     
    214806    }
    215807
    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() {
    217815      Event evt;
    218       lock (locker) {
     816      lock (_locker) {
    219817        var next = ScheduleQ.Dequeue();
    220818        Now = next.PrimaryPriority;
     
    224822    }
    225823
    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) {
    228832        if (ScheduleQ.Count == 0) return double.MaxValue;
    229833        return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds;
     
    231835    }
    232836
    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) {
    235845        return ScheduleQ.Count > 0 ? ScheduleQ.First.PrimaryPriority : DateTime.MaxValue;
    236846      }
    237847    }
    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
    249876    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) {
    307878      double z, zz, u1, u2;
    308879      do {
    309         u1 = Random.NextDouble();
    310         u2 = 1 - Random.NextDouble();
     880        u1 = random.NextDouble();
     881        u2 = 1 - random.NextDouble();
    311882        z = NormalMagicConst * (u1 - 0.5) / u2;
    312883        zz = z * z / 4.0;
     
    314885      return mu + z * sigma;
    315886    }
    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     #endregion
    369 
    370     #region Random timeouts
    371     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     #endregion
    419887  }
    420888}
Note: See TracChangeset for help on using the changeset viewer.