Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.9/SimSharp-3.0.9/Core/Environment.cs @ 14196

Last change on this file since 14196 was 14196, checked in by abeham, 8 years ago

#2644: upgraded Sim# version from 3.0.7 to 3.0.9

File size: 13.9 KB
Line 
1#region License Information
2/* SimSharp - A .NET port of SimPy, discrete event simulation framework
3Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
17#endregion
18
19using System;
20using System.Collections.Generic;
21using System.IO;
22
23namespace SimSharp {
24  /// <summary>
25  /// Environments hold the event queues, schedule and process events.
26  /// </summary>
27  public class Environment {
28    private const int InitialMaxEvents = 1024;
29    private object locker = new object();
30
31    /// <summary>
32    /// Describes the number of seconds that a logical step of 1 in the *D-API takes.
33    /// </summary>
34    protected double DefaultTimeStepSeconds { get; private set; }
35
36    /// <summary>
37    /// Calculates the logical date of the simulation by the amount of default steps
38    /// that have passed.
39    /// </summary>
40    public double NowD {
41      get { return (Now - StartDate).TotalSeconds / DefaultTimeStepSeconds; }
42    }
43
44    /// <summary>
45    /// The current simulation time as a calendar date.
46    /// </summary>
47    public DateTime Now { get; protected set; }
48
49    /// <summary>
50    /// The calendar date when the simulation started. This defaults to 1970-1-1 if
51    /// no other date has been specified in the overloaded constructor.
52    /// </summary>
53    public DateTime StartDate { get; protected set; }
54
55    /// <summary>
56    /// The random number generator that is to be used in all events in
57    /// order to produce reproducible results.
58    /// </summary>
59    protected IRandom Random { get; set; }
60
61    protected EventQueue ScheduleQ;
62    protected Queue<Event> Queue;
63    public Process ActiveProcess { get; set; }
64
65    public TextWriter Logger { get; set; }
66    public int ProcessedEvents { get; protected set; }
67
68    public Environment() : this(new DateTime(1970, 1, 1)) { }
69    public Environment(TimeSpan? defaultStep) : this(new DateTime(1970, 1, 1), defaultStep) { }
70    public Environment(int randomSeed, TimeSpan? defaultStep = null) : this(new DateTime(1970, 1, 1), randomSeed, defaultStep) { }
71    public Environment(DateTime initialDateTime, TimeSpan? defaultStep = null) {
72      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
73      StartDate = initialDateTime;
74      Now = initialDateTime;
75      Random = new SystemRandom();
76      ScheduleQ = new EventQueue(InitialMaxEvents);
77      Queue = new Queue<Event>();
78      Logger = Console.Out;
79    }
80    public Environment(DateTime initialDateTime, int randomSeed, TimeSpan? defaultStep = null) {
81      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
82      StartDate = initialDateTime;
83      Now = initialDateTime;
84      Random = new SystemRandom(randomSeed);
85      ScheduleQ = new EventQueue(InitialMaxEvents);
86      Queue = new Queue<Event>();
87      Logger = Console.Out;
88    }
89
90    public double ToDouble(TimeSpan span) {
91      return span.TotalSeconds / DefaultTimeStepSeconds;
92    }
93
94    public TimeSpan ToTimeSpan(double span) {
95      return TimeSpan.FromSeconds(DefaultTimeStepSeconds * span);
96    }
97
98    public Process Process(IEnumerable<Event> generator) {
99      return new Process(this, generator);
100    }
101
102    public Timeout TimeoutD(double delay) {
103      return Timeout(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay));
104    }
105
106    public Timeout Timeout(TimeSpan delay) {
107      return new Timeout(this, delay);
108    }
109
110    public virtual void Reset(int randomSeed) {
111      ProcessedEvents = 0;
112      Now = StartDate;
113      Random = new SystemRandom(randomSeed);
114      ScheduleQ = new EventQueue(InitialMaxEvents);
115      Queue = new Queue<Event>();
116    }
117
118    public virtual void ScheduleD(double delay, Event @event) {
119      Schedule(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), @event);
120    }
121
122    public virtual void Schedule(Event @event) {
123      lock (locker) {
124        Queue.Enqueue(@event);
125      }
126    }
127
128    public virtual void Schedule(TimeSpan delay, Event @event) {
129      if (delay < TimeSpan.Zero)
130        throw new ArgumentException("Negative delays are not allowed in Schedule(TimeSpan, Event).");
131      lock (locker) {
132        if (delay == TimeSpan.Zero) {
133          Queue.Enqueue(@event);
134          return;
135        }
136        var eventTime = Now + delay;
137        DoSchedule(eventTime, @event);
138      }
139    }
140
141    protected virtual EventQueueNode DoSchedule(DateTime date, Event @event) {
142      if (ScheduleQ.MaxSize == ScheduleQ.Count) {
143        // the capacity has to be adjusted, there are more events in the queue than anticipated
144        var oldSchedule = ScheduleQ;
145        ScheduleQ = new EventQueue(ScheduleQ.MaxSize * 2);
146        foreach (var e in oldSchedule) ScheduleQ.Enqueue(e.Priority, e.Event);
147      }
148      return ScheduleQ.Enqueue(date, @event);
149    }
150
151    public virtual object RunD(double? until = null) {
152      if (!until.HasValue) return Run();
153      return Run(Now + TimeSpan.FromSeconds(DefaultTimeStepSeconds * until.Value));
154    }
155
156    public virtual object Run(TimeSpan span) {
157      return Run(Now + span);
158    }
159
160    public virtual object Run(DateTime until) {
161      if (until <= Now) throw new InvalidOperationException("Simulation end date must lie in the future.");
162      var stopEvent = new Event(this);
163      var node = DoSchedule(until, stopEvent);
164      // stop event is always the first to execute at the given time
165      node.InsertionIndex = -1;
166      ScheduleQ.OnNodeUpdated(node);
167      return Run(stopEvent);
168    }
169
170    public virtual object Run(Event stopEvent = null) {
171      if (stopEvent != null) {
172        if (stopEvent.IsProcessed) return stopEvent.Value;
173        stopEvent.AddCallback(StopSimulation);
174      }
175
176      try {
177        var stop = Queue.Count == 0 && ScheduleQ.Count == 0;
178        while (!stop) {
179          Step();
180          ProcessedEvents++;
181          lock (locker) {
182            stop = Queue.Count == 0 && ScheduleQ.Count == 0;
183          }
184        }
185      } catch (StopSimulationException e) { return e.Value; }
186      if (stopEvent == null) return null;
187      if (!stopEvent.IsTriggered) throw new InvalidOperationException("No scheduled events left but \"until\" event was not triggered.");
188      return stopEvent.Value;
189    }
190
191    public virtual void Step() {
192      Event evt;
193      lock (locker) {
194        if (Queue.Count == 0) {
195          var next = ScheduleQ.Dequeue();
196          Now = next.Priority;
197          evt = next.Event;
198        } else evt = Queue.Dequeue();
199      }
200      evt.Process();
201    }
202
203    public virtual double PeekD() {
204      lock (locker) {
205        if (Queue.Count == 0 && ScheduleQ.Count == 0) return double.MaxValue;
206        return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds;
207      }
208    }
209
210    public virtual DateTime Peek() {
211      lock (locker) {
212        return Queue.Count > 0 ? Now : (ScheduleQ.Count > 0 ? ScheduleQ.First.Priority : DateTime.MaxValue);
213      }
214    }
215
216    protected virtual void StopSimulation(Event @event) {
217      throw new StopSimulationException(@event.Value);
218    }
219
220    public virtual void Log(string message, params object[] args) {
221      if (Logger != null)
222        Logger.WriteLine(message, args);
223    }
224
225    #region Random number distributions
226    protected static readonly double NormalMagicConst = 4 * Math.Exp(-0.5) / Math.Sqrt(2.0);
227
228    public double RandUniform(double a, double b) {
229      return a + (b - a) * Random.NextDouble();
230    }
231
232    public TimeSpan RandUniform(TimeSpan a, TimeSpan b) {
233      return TimeSpan.FromSeconds(RandUniform(a.TotalSeconds, b.TotalSeconds));
234    }
235
236    public double RandTriangular(double low, double high) {
237      var u = Random.NextDouble();
238      if (u > 0.5)
239        return high + (low - high) * Math.Sqrt(((1.0 - u) / 2));
240      return low + (high - low) * Math.Sqrt(u / 2);
241    }
242
243    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high) {
244      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds));
245    }
246
247    public double RandTriangular(double low, double high, double mode) {
248      var u = Random.NextDouble();
249      var c = (mode - low) / (high - low);
250      if (u > c)
251        return high + (low - high) * Math.Sqrt(((1.0 - u) * (1.0 - c)));
252      return low + (high - low) * Math.Sqrt(u * c);
253    }
254
255    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
256      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds, mode.TotalSeconds));
257    }
258
259    /// <summary>
260    /// Returns a number that is exponentially distributed given a certain mean.
261    /// </summary>
262    /// <remarks>
263    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
264    /// </remarks>
265    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
266    /// <returns>A number that is exponentially distributed</returns>
267    public double RandExponential(double mean) {
268      return -Math.Log(1 - Random.NextDouble()) * mean;
269    }
270
271    /// <summary>
272    /// Returns a timespan that is exponentially distributed given a certain mean.
273    /// </summary>
274    /// <remarks>
275    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
276    /// </remarks>
277    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
278    /// <returns>A number that is exponentially distributed</returns>
279    public TimeSpan RandExponential(TimeSpan mean) {
280      return TimeSpan.FromSeconds(RandExponential(mean.TotalSeconds));
281    }
282
283    public double RandNormal(double mu, double sigma) {
284      double z, zz, u1, u2;
285      do {
286        u1 = Random.NextDouble();
287        u2 = 1 - Random.NextDouble();
288        z = NormalMagicConst * (u1 - 0.5) / u2;
289        zz = z * z / 4.0;
290      } while (zz > -Math.Log(u2));
291      return mu + z * sigma;
292    }
293
294    public TimeSpan RandNormal(TimeSpan mu, TimeSpan sigma) {
295      return TimeSpan.FromSeconds(RandNormal(mu.TotalSeconds, sigma.TotalSeconds));
296    }
297
298    public double RandNormalPositive(double mu, double sigma) {
299      double val;
300      do {
301        val = RandNormal(mu, sigma);
302      } while (val <= 0);
303      return val;
304    }
305
306    public TimeSpan RandNormalPositive(TimeSpan mu, TimeSpan sigma) {
307      return TimeSpan.FromSeconds(RandNormalPositive(mu.TotalSeconds, sigma.TotalSeconds));
308    }
309
310    public double RandNormalNegative(double mu, double sigma) {
311      double val;
312      do {
313        val = RandNormal(mu, sigma);
314      } while (val >= 0);
315      return val;
316    }
317
318    public TimeSpan RandNormalNegative(TimeSpan mu, TimeSpan sigma) {
319      return TimeSpan.FromSeconds(RandNormalNegative(mu.TotalSeconds, sigma.TotalSeconds));
320    }
321
322    public double RandLogNormal(double mu, double sigma) {
323      return Math.Exp(RandNormal(mu, sigma));
324    }
325
326    public TimeSpan RandLogNormal(TimeSpan mu, TimeSpan sigma) {
327      return TimeSpan.FromSeconds(RandLogNormal(mu.TotalSeconds, sigma.TotalSeconds));
328    }
329
330    public double RandCauchy(double x0, double gamma) {
331      return x0 + gamma * Math.Tan(Math.PI * (Random.NextDouble() - 0.5));
332    }
333
334    public TimeSpan RandCauchy(TimeSpan x0, TimeSpan gamma) {
335      return TimeSpan.FromSeconds(RandCauchy(x0.TotalSeconds, gamma.TotalSeconds));
336    }
337
338    public double RandWeibull(double alpha, double beta) {
339      return alpha * Math.Pow(-Math.Log(1 - Random.NextDouble()), 1 / beta);
340    }
341
342    public TimeSpan RandWeibull(TimeSpan mu, TimeSpan sigma) {
343      return TimeSpan.FromSeconds(RandWeibull(mu.TotalSeconds, sigma.TotalSeconds));
344    }
345    #endregion
346
347    #region Random timeouts
348    public Timeout TimeoutUniformD(double a, double b) {
349      return new Timeout(this, ToTimeSpan(RandUniform(a, b)));
350    }
351
352    public Timeout TimeoutUniform(TimeSpan a, TimeSpan b) {
353      return new Timeout(this, RandUniform(a, b));
354    }
355
356    public Timeout TimeoutTriangularD(double low, double high) {
357      return new Timeout(this, ToTimeSpan(RandTriangular(low, high)));
358    }
359
360    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high) {
361      return new Timeout(this, RandTriangular(low, high));
362    }
363
364    public Timeout TimeoutTriangularD(double low, double high, double mode) {
365      return new Timeout(this, ToTimeSpan(RandTriangular(low, high, mode)));
366    }
367
368    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
369      return new Timeout(this, RandTriangular(low, high, mode));
370    }
371
372    public Timeout TimeoutExponentialD(double mean) {
373      return new Timeout(this, ToTimeSpan(RandExponential(mean)));
374    }
375
376    public Timeout TimeoutExponential(TimeSpan mean) {
377      return new Timeout(this, RandExponential(mean));
378    }
379
380    public Timeout TimeoutNormalPositiveD(double mu, double sigma) {
381      return new Timeout(this, ToTimeSpan(RandNormalPositive(mu, sigma)));
382    }
383
384    public Timeout TimeoutNormalPositive(TimeSpan mu, TimeSpan sigma) {
385      return new Timeout(this, RandNormalPositive(mu, sigma));
386    }
387
388    public Timeout TimeoutLogNormalD(double mu, double sigma) {
389      return new Timeout(this, ToTimeSpan(RandLogNormal(mu, sigma)));
390    }
391
392    public Timeout TimeoutLogNormal(TimeSpan mu, TimeSpan sigma) {
393      return new Timeout(this, RandLogNormal(mu, sigma));
394    }
395    #endregion
396  }
397}
Note: See TracBrowser for help on using the repository browser.