Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2931_OR-Tools_LP_MIP/HeuristicLab.ExtLibs/HeuristicLab.SimSharp/3.0.11/SimSharp-3.0.11/Core/Environment.cs @ 16070

Last change on this file since 16070 was 15972, checked in by abeham, 6 years ago

#2926: Replaced Sim# 3.0.9 with 3.0.11

File size: 15.7 KB
Line 
1#region License Information
2/* SimSharp - A .NET port of SimPy, discrete event simulation framework
3Copyright (C) 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    public Process ActiveProcess { get; set; }
63
64    public TextWriter Logger { get; set; }
65    public int ProcessedEvents { get; protected set; }
66
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) {
71      DefaultTimeStepSeconds = (defaultStep ?? TimeSpan.FromSeconds(1)).Duration().TotalSeconds;
72      StartDate = initialDateTime;
73      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);
83      ScheduleQ = new EventQueue(InitialMaxEvents);
84      Logger = Console.Out;
85    }
86
87    public double ToDouble(TimeSpan span) {
88      return span.TotalSeconds / DefaultTimeStepSeconds;
89    }
90
91    public TimeSpan ToTimeSpan(double span) {
92      return TimeSpan.FromSeconds(DefaultTimeStepSeconds * span);
93    }
94
95    /// <summary>
96    /// Creates a new process from an event generator. The process is automatically
97    /// scheduled to be started at the current simulation time.
98    /// </summary>
99    /// <param name="generator">The generator function that represents the process.</param>
100    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
101    /// <returns>The scheduled process that was created.</returns>
102    public Process Process(IEnumerable<Event> generator, int priority = 0) {
103      return new Process(this, generator, priority);
104    }
105
106    /// <summary>
107    /// Creates and returns a new timeout.
108    /// </summary>
109    /// <param name="delay">The time after which the timeout is fired.</param>
110    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
111    /// <returns>The scheduled timeout event that was created.</returns>
112    public Timeout TimeoutD(double delay, int priority = 0) {
113      return Timeout(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), priority);
114    }
115
116    /// <summary>
117    /// Creates and returns a new timeout.
118    /// </summary>
119    /// <param name="delay">The time after which the timeout is fired.</param>
120    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
121    /// <returns>The scheduled timeout event that was created.</returns>
122    public Timeout Timeout(TimeSpan delay, int priority = 0) {
123      return new Timeout(this, delay, priority: priority);
124    }
125
126    public virtual void Reset(int randomSeed) {
127      ProcessedEvents = 0;
128      Now = StartDate;
129      Random = new SystemRandom(randomSeed);
130      ScheduleQ = new EventQueue(InitialMaxEvents);
131    }
132
133    public virtual void ScheduleD(double delay, Event @event) {
134      Schedule(TimeSpan.FromSeconds(DefaultTimeStepSeconds * delay), @event);
135    }
136
137    /// <summary>
138    /// Schedules an event to occur at the same simulation time as the call was made.
139    /// </summary>
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>
142    public virtual void Schedule(Event @event, int priority = 0) {
143      lock (locker) {
144        DoSchedule(Now, @event, priority);
145      }
146    }
147
148    /// <summary>
149    /// Schedules an event to occur after a certain (positive) delay.
150    /// </summary>
151    /// <exception cref="ArgumentException">
152    /// Thrown when <paramref name="delay"/> is negative.
153    /// </exception>
154    /// <param name="delay">The (positive) delay after which the event should be fired.</param>
155    /// <param name="event">The event that should be scheduled.</param>
156    /// <param name="priority">The priority to rank events at the same time (smaller value = higher priority).</param>
157    public virtual void Schedule(TimeSpan delay, Event @event, int priority = 0) {
158      if (delay < TimeSpan.Zero)
159        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      }
164    }
165
166    protected virtual EventQueueNode DoSchedule(DateTime date, Event @event, int priority = 0) {
167      if (ScheduleQ.MaxSize == ScheduleQ.Count) {
168        // the capacity has to be adjusted, there are more events in the queue than anticipated
169        var oldSchedule = ScheduleQ;
170        ScheduleQ = new EventQueue(ScheduleQ.MaxSize * 2);
171        foreach (var e in oldSchedule) ScheduleQ.Enqueue(e.PrimaryPriority, e.Event, e.SecondaryPriority);
172      }
173      return ScheduleQ.Enqueue(date, @event, priority);
174    }
175
176    public virtual object RunD(double? until = null) {
177      if (!until.HasValue) return Run();
178      return Run(Now + TimeSpan.FromSeconds(DefaultTimeStepSeconds * until.Value));
179    }
180
181    public virtual object Run(TimeSpan span) {
182      return Run(Now + span);
183    }
184
185    public virtual object Run(DateTime until) {
186      if (until <= Now) throw new InvalidOperationException("Simulation end date must lie in the future.");
187      var stopEvent = new Event(this);
188      var node = DoSchedule(until, stopEvent);
189      // stop event is always the first to execute at the given time
190      node.InsertionIndex = -1;
191      ScheduleQ.OnNodeUpdated(node);
192      return Run(stopEvent);
193    }
194
195    public virtual object Run(Event stopEvent = null) {
196      if (stopEvent != null) {
197        if (stopEvent.IsProcessed) return stopEvent.Value;
198        stopEvent.AddCallback(StopSimulation);
199      }
200
201      try {
202        var stop = ScheduleQ.Count == 0;
203        while (!stop) {
204          Step();
205          ProcessedEvents++;
206          lock (locker) {
207            stop = ScheduleQ.Count == 0;
208          }
209        }
210      } catch (StopSimulationException e) { return e.Value; }
211      if (stopEvent == null) return null;
212      if (!stopEvent.IsTriggered) throw new InvalidOperationException("No scheduled events left but \"until\" event was not triggered.");
213      return stopEvent.Value;
214    }
215
216    public virtual void Step() {
217      Event evt;
218      lock (locker) {
219        var next = ScheduleQ.Dequeue();
220        Now = next.PrimaryPriority;
221        evt = next.Event;
222      }
223      evt.Process();
224    }
225
226    public virtual double PeekD() {
227      lock (locker) {
228        if (ScheduleQ.Count == 0) return double.MaxValue;
229        return (Peek() - StartDate).TotalSeconds / DefaultTimeStepSeconds;
230      }
231    }
232
233    public virtual DateTime Peek() {
234      lock (locker) {
235        return ScheduleQ.Count > 0 ? ScheduleQ.First.PrimaryPriority : DateTime.MaxValue;
236      }
237    }
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
249    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) {
307      double z, zz, u1, u2;
308      do {
309        u1 = Random.NextDouble();
310        u2 = 1 - Random.NextDouble();
311        z = NormalMagicConst * (u1 - 0.5) / u2;
312        zz = z * z / 4.0;
313      } while (zz > -Math.Log(u2));
314      return mu + z * sigma;
315    }
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
419  }
420}
Note: See TracBrowser for help on using the repository browser.