Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.7/SimSharp-3.0.7/Core/Environment.cs @ 12657

Last change on this file since 12657 was 12657, checked in by abeham, 9 years ago

#2420: Added Sim# as an ExtLib plugin

File size: 13.9 KB
Line 
1#region License Information
2/* SimSharp - A .NET port of SimPy, discrete event simulation framework
3Copyright (C) 2014  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      Now = StartDate;
112      Random = new SystemRandom(randomSeed);
113      ScheduleQ = new EventQueue(InitialMaxEvents);
114      Queue = new Queue<Event>();
115    }
116
117    public virtual void ScheduleD(double delay, Event @event) {
118      Schedule(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), @event);
119    }
120
121    public virtual void Schedule(Event @event) {
122      lock (locker) {
123        Queue.Enqueue(@event);
124      }
125    }
126
127    public virtual void Schedule(TimeSpan delay, Event @event) {
128      if (delay < TimeSpan.Zero)
129        throw new ArgumentException("Negative delays are not allowed in Schedule(TimeSpan, Event).");
130      lock (locker) {
131        if (delay == TimeSpan.Zero) {
132          Queue.Enqueue(@event);
133          return;
134        }
135        var eventTime = Now + delay;
136        DoSchedule(eventTime, @event);
137      }
138    }
139
140    protected virtual EventQueueNode DoSchedule(DateTime date, Event @event) {
141      if (ScheduleQ.MaxSize == ScheduleQ.Count) {
142        // the capacity has to be adjusted, there are more events in the queue than anticipated
143        var oldSchedule = ScheduleQ;
144        ScheduleQ = new EventQueue(ScheduleQ.MaxSize * 2);
145        foreach (var e in oldSchedule) ScheduleQ.Enqueue(e.Priority, e.Event);
146      }
147      return ScheduleQ.Enqueue(date, @event);
148    }
149
150    public virtual object RunD(double? until = null) {
151      if (!until.HasValue) return Run();
152      return Run(Now + TimeSpan.FromSeconds(DefaultTimeStepSeconds * until.Value));
153    }
154
155    public virtual object Run(TimeSpan span) {
156      return Run(Now + span);
157    }
158
159    public virtual object Run(DateTime until) {
160      if (until <= Now) throw new InvalidOperationException("Simulation end date must lie in the future.");
161      var stopEvent = new Event(this);
162      var node = DoSchedule(until, stopEvent);
163      // stop event is always the first to execute at the given time
164      node.InsertionIndex = -1;
165      ScheduleQ.OnNodeUpdated(node);
166      return Run(stopEvent);
167    }
168
169    public virtual object Run(Event stopEvent = null) {
170      if (stopEvent != null) {
171        if (stopEvent.IsProcessed) return stopEvent.Value;
172        stopEvent.AddCallback(StopSimulation);
173      }
174
175      try {
176        var stop = Queue.Count == 0 && ScheduleQ.Count == 0;
177        while (!stop) {
178          Step();
179          ProcessedEvents++;
180          lock (locker) {
181            stop = Queue.Count == 0 && ScheduleQ.Count == 0;
182          }
183        }
184      } catch (StopSimulationException e) { return e.Value; }
185      if (stopEvent == null) return null;
186      if (!stopEvent.IsTriggered) throw new InvalidOperationException("No scheduled events left but \"until\" event was not triggered.");
187      return stopEvent.Value;
188    }
189
190    public virtual void Step() {
191      Event evt;
192      lock (locker) {
193        if (Queue.Count == 0) {
194          var next = ScheduleQ.Dequeue();
195          Now = next.Priority;
196          evt = next.Event;
197        } else evt = Queue.Dequeue();
198      }
199      evt.Process();
200    }
201
202    public virtual double PeekD() {
203      lock (locker) {
204        if (Queue.Count == 0 && ScheduleQ.Count == 0) return double.MaxValue;
205        return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds;
206      }
207    }
208
209    public virtual DateTime Peek() {
210      lock (locker) {
211        return Queue.Count > 0 ? Now : (ScheduleQ.Count > 0 ? ScheduleQ.First.Priority : DateTime.MaxValue);
212      }
213    }
214
215    protected virtual void StopSimulation(Event @event) {
216      throw new StopSimulationException(@event.Value);
217    }
218
219    public virtual void Log(string message, params object[] args) {
220      if (Logger != null)
221        Logger.WriteLine(message, args);
222    }
223
224    #region Random number distributions
225    protected static readonly double NormalMagicConst = 4 * Math.Exp(-0.5) / Math.Sqrt(2.0);
226
227    public double RandUniform(double a, double b) {
228      return a + (b - a) * Random.NextDouble();
229    }
230
231    public TimeSpan RandUniform(TimeSpan a, TimeSpan b) {
232      return TimeSpan.FromSeconds(RandUniform(a.TotalSeconds, b.TotalSeconds));
233    }
234
235    public double RandTriangular(double low, double high) {
236      var u = Random.NextDouble();
237      if (u > 0.5)
238        return high + (low - high) * Math.Sqrt(((1.0 - u) / 2));
239      return low + (high - low) * Math.Sqrt(u / 2);
240    }
241
242    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high) {
243      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds));
244    }
245
246    public double RandTriangular(double low, double high, double mode) {
247      var u = Random.NextDouble();
248      var c = (mode - low) / (high - low);
249      if (u > c)
250        return high + (low - high) * Math.Sqrt(((1.0 - u) * (1.0 - c)));
251      return low + (high - low) * Math.Sqrt(u * c);
252    }
253
254    public TimeSpan RandTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
255      return TimeSpan.FromSeconds(RandTriangular(low.TotalSeconds, high.TotalSeconds, mode.TotalSeconds));
256    }
257
258    /// <summary>
259    /// Returns a number that is exponentially distributed given a certain mean.
260    /// </summary>
261    /// <remarks>
262    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
263    /// </remarks>
264    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
265    /// <returns>A number that is exponentially distributed</returns>
266    public double RandExponential(double mean) {
267      return -Math.Log(1 - Random.NextDouble()) * mean;
268    }
269
270    /// <summary>
271    /// Returns a timespan that is exponentially distributed given a certain mean.
272    /// </summary>
273    /// <remarks>
274    /// Unlike in other APIs here the mean should be given and not the lambda parameter.
275    /// </remarks>
276    /// <param name="mean">The mean(!) of the distribution is 1 / lambda.</param>
277    /// <returns>A number that is exponentially distributed</returns>
278    public TimeSpan RandExponential(TimeSpan mean) {
279      return TimeSpan.FromSeconds(RandExponential(mean.TotalSeconds));
280    }
281
282    public double RandNormal(double mu, double sigma) {
283      double z, zz, u1, u2;
284      do {
285        u1 = Random.NextDouble();
286        u2 = 1 - Random.NextDouble();
287        z = NormalMagicConst * (u1 - 0.5) / u2;
288        zz = z * z / 4.0;
289      } while (zz > -Math.Log(u2));
290      return mu + z * sigma;
291    }
292
293    public TimeSpan RandNormal(TimeSpan mu, TimeSpan sigma) {
294      return TimeSpan.FromSeconds(RandNormal(mu.TotalSeconds, sigma.TotalSeconds));
295    }
296
297    public double RandNormalPositive(double mu, double sigma) {
298      double val;
299      do {
300        val = RandNormal(mu, sigma);
301      } while (val <= 0);
302      return val;
303    }
304
305    public TimeSpan RandNormalPositive(TimeSpan mu, TimeSpan sigma) {
306      return TimeSpan.FromSeconds(RandNormalPositive(mu.TotalSeconds, sigma.TotalSeconds));
307    }
308
309    public double RandNormalNegative(double mu, double sigma) {
310      double val;
311      do {
312        val = RandNormal(mu, sigma);
313      } while (val >= 0);
314      return val;
315    }
316
317    public TimeSpan RandNormalNegative(TimeSpan mu, TimeSpan sigma) {
318      return TimeSpan.FromSeconds(RandNormalNegative(mu.TotalSeconds, sigma.TotalSeconds));
319    }
320
321    public double RandLogNormal(double mu, double sigma) {
322      return Math.Exp(RandNormal(mu, sigma));
323    }
324
325    public TimeSpan RandLogNormal(TimeSpan mu, TimeSpan sigma) {
326      return TimeSpan.FromSeconds(RandLogNormal(mu.TotalSeconds, sigma.TotalSeconds));
327    }
328
329    public double RandCauchy(double x0, double gamma) {
330      return x0 + gamma * Math.Tan(Math.PI * (Random.NextDouble() - 0.5));
331    }
332
333    public TimeSpan RandCauchy(TimeSpan x0, TimeSpan gamma) {
334      return TimeSpan.FromSeconds(RandCauchy(x0.TotalSeconds, gamma.TotalSeconds));
335    }
336
337    public double RandWeibull(double alpha, double beta) {
338      return alpha * Math.Pow(-Math.Log(1 - Random.NextDouble()), 1 / beta);
339    }
340
341    public TimeSpan RandWeibull(TimeSpan mu, TimeSpan sigma) {
342      return TimeSpan.FromSeconds(RandWeibull(mu.TotalSeconds, sigma.TotalSeconds));
343    }
344    #endregion
345
346    #region Random timeouts
347    public Timeout TimeoutUniformD(double a, double b) {
348      return new Timeout(this, ToTimeSpan(RandUniform(a, b)));
349    }
350
351    public Timeout TimeoutUniform(TimeSpan a, TimeSpan b) {
352      return new Timeout(this, RandUniform(a, b));
353    }
354
355    public Timeout TimeoutTriangularD(double low, double high) {
356      return new Timeout(this, ToTimeSpan(RandTriangular(low, high)));
357    }
358
359    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high) {
360      return new Timeout(this, RandTriangular(low, high));
361    }
362
363    public Timeout TimeoutTriangularD(double low, double high, double mode) {
364      return new Timeout(this, ToTimeSpan(RandTriangular(low, high, mode)));
365    }
366
367    public Timeout TimeoutTriangular(TimeSpan low, TimeSpan high, TimeSpan mode) {
368      return new Timeout(this, RandTriangular(low, high, mode));
369    }
370
371    public Timeout TimeoutExponentialD(double mean) {
372      return new Timeout(this, ToTimeSpan(RandExponential(mean)));
373    }
374
375    public Timeout TimeoutExponential(TimeSpan mean) {
376      return new Timeout(this, RandExponential(mean));
377    }
378
379    public Timeout TimeoutNormalPositiveD(double mu, double sigma) {
380      return new Timeout(this, ToTimeSpan(RandNormalPositive(mu, sigma)));
381    }
382
383    public Timeout TimeoutNormalPositive(TimeSpan mu, TimeSpan sigma) {
384      return new Timeout(this, RandNormalPositive(mu, sigma));
385    }
386
387    public Timeout TimeoutLogNormalD(double mu, double sigma) {
388      return new Timeout(this, ToTimeSpan(RandLogNormal(mu, sigma)));
389    }
390
391    public Timeout TimeoutLogNormal(TimeSpan mu, TimeSpan sigma) {
392      return new Timeout(this, RandLogNormal(mu, sigma));
393    }
394    #endregion
395  }
396}
Note: See TracBrowser for help on using the repository browser.