forked from salabim/salabim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchangelog.txt
4067 lines (3269 loc) · 173 KB
/
changelog.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
salabim changelog
version 20.0.3 2020-08-06
==========================
New functionality (0)
-----------------------
The specification of sim.Distribution now also accepts a time_unit parameter.
Note that if the specification string contains a time_unit parameter as well, the time_unit parameter
of Distribution is ignored.
Examples
d = sim.Distribution('uniform(1, 2)', time_unit='minutes')) # 1-2 minutes
d = sim.Distribution('uniform(1, 2, time_unit='hours')')) # 1-2 hours, same as before
d = sim.Distribution('uniform(1, 2, time_unit='hours')', time_unit='minutes')) # 1-2 hours, ignore minutes
New functionality (1)
-----------------------
Monitor.freeze() returns a 'frozen' monitor that can be used to store the results not
depending on the current environment.
This is particularly useful for pickling a monitor.
E.g. use
with open("mon.pickle", "wb") as f:
pickle.dump(f, mon.freeze())
to save the monitor mon, and
with open("mon.pickle", "rb") as f:
mon_retrieved = pickle.load(f)
to retrieve the monitor, later.
Both level and non level monitors are supported.
Frozen monitors get the name of the original monitor padded with '.frozen' unless specified differently.
New functionality (2)
---------------------
All Component methods that support urgent to schedule a component now also support a priority parameter.
With this it is possible to sort a component before or after other components, scheduled for the same time.
Note that urgent only applies to components scheduled with the same time and same priority.
The priority is 0 by default.
This is particularly useful for race conditions. It is possible to change the priority of a component
by cancelling it prior to activating it with another priority.
The priority can be accessed with the new Component.scheduled_priority() method.
Improved functionality (0)
--------------------------
Video production can now be done with a context manager, thus making an explicit final call to video_close
obsolete:
with env.video('myvideo.mp4'):
...
env.run(10)
This will automatically close the file myvideo.mp4 upon leaving the with block.
Change in functionality (0)
---------------------------
In sim.reset(), which is always called at startup, random_seed() will be called without any parameters, causing
the random_seed to be set to 1234567.
That makes reproducibility even possible when calling
env = sim.Environment(random_seed="") # no change in seed
If 'real' random behaviour (dependent on clock ticks) is required, just do:
env = sim.Environment(random_seed="*")
Changes in functionality (1)
----------------------------
Queue.extend() does return None now instead of a copy of self, for consistency reasons.
Now queues support all comparisons, i.e. ==, !=, <, <=, >, >=
These comparsons are on membership only, i.e they ignore, the order, priorities and name.
It is possible to compare a quueu with any object supporting the iter protocol, most notably
sets, lists and tuples.
Internal changes /change in functionality (0)
---------------------------------------------
Upon termination of a process, salabim did check a list to see whether there were any animation objects
that had this component as it parent. Particularly with many animation objects defined that could take a long
time, as reported by Hugo Huges.
From this version on, a component itself has a set of its 'animation children'.
Also changed is the moment the animation child is removed automatically. When a parent (component)
is no longer accessible, it will remove all of its animation children if any.
That means we now rely on the automatic garbage collection, which can be slightly delayed.
This change means that an animation object with a parent that terminates its process is not
necessarily removed, as it can be still in a queue, or even just referenced by a variable.
If you use the parent parameter in an Animate, AnimateQueue or AnimateMonitor this might change the
behaviour.
Added support files (0)
-----------------------
For the development there's is now a set of pytest files that will result in a more stable product. Indeed
several bugs (see below) were detected during this test development.
Future versions will include more test scripts.
Bug fix (0)
-----------
Minor bug in 'install salabim from github.py' and 'install salabim.py' fixed.
Bug fix (1)
-----------
Bug in Component.wait() fixed. Thank you, Alexander Kaiblinger, for detecting the bug and proposing the solution.
Bug fix (2)
-----------
Upon honouring an anonymous resource, the statistics of the available_quantity, claimed_quantity and occupancy
were not updated. Bug fixed. Thank you, Lukas Hollenstein, for detecting the bug and proposing the solution.
Bug fix (3)
-----------
The algorithm used to calculate weighted percentiles in Monitor.percentile() from a stackoverflow.com article
was found to be incorrect, thus sometimes resulting in wrong percentiles.
Bug fixed.
Bug fix (4)
-----------
Minor changes in Queue.__iter__ and Queue.__reversed__ to make iterating when components are added or removed
during the iteration more consistent.
version 20.0.2 2020-05-18
==========================
New functionality (0)
---------------------
Component.request() has a new parameter 'oneof'.
If oneof=True, the request has to be honoured by just one of the given resources.
So, this essentially an or condition.
Note that the order of the mentioned resources is the order in which the request will be honoured.
It is possible to check which resource has claimed with Component.claimers()
Example:
c.request(r1, r2, r3, oneof=True)
The request will be honoured if either r1, r2 OR r3 has at least a quantity of one available.
This contrast to
c.request(r1, r2, r3)
, which will be honoured if r1, r2 AND r3 have at least a quantity of one available.
The trace of request and honouring request has been updated accordingly.
Changes in video production (0)
-------------------------------
With this version, animated PNGs are supported as well. In contrast to animated GIFs, the background
can be transparent (i.e. have an alpha <255).
For the production of individual video frames in jpg, png, bmp, tiff or gif format, the filename
now has to contain one asterisk (*), which will be replaced by a 6 digit zero-padded serial number
at run time, for each frame.
E.g.
video('.\videos\car*.jpg)
Filenames without an asteriks are only allowed for real videos or animated gif/png files:
videos('.videos\car.png')
Note that the directory is now automatically created.
Because of the changes, the Environment.delete_video() is now deprecated.
Internally, the video production is now more consistent and easier to maintain.
Improved functionality (0)
--------------------------
Under Linux and Android many more fonts will be available because salabim now searches (recursively)
in /usr/share/fonts and /system/fonts for .ttf files as well.
As a reminder: it is possible to show all available fonts with
sim.show_fonts()
Utility files (0)
-----------------
The utility 'install.py' is now called 'install salabim.py' and is now fully compatible with
pip uninstall and pip update, because a salabim-<version>.dist-info directory is written correctly
to the site-packages folder.
New is 'install salabim from github.py' which installs salabim directly from github instead of PyPI.
Support for iPadOS/iOS PyTo (0)
-------------------------------
When running under PyTo, a NotImplementedError was raised. Now, salabim can be run on this platform, albeit
animation is not supported.
Compatibility (0)
-----------------
From now on, only Python >=3.4 is supported, particularly because salabim now uses pathlib internally.
Bugfix (0)
-----------
In a number of methods, e.g. Queue.__init__(), the environment of an implied Monitor was set to the
default environment instead of the overruled environment.
Bugfix (1)
-----------
PeriodMonitor lacked an env parameter. Fixed.
Bugfix (2)
-----------
Under certain conditions, an animated GIF was not written correctly as a result of a bug in the optimization
of PIL save. From now on, salabim disables the optimization, possibly resulting in slightly larger
.GIF files. This change does not apply to Pythonista, which uses another technique to save animated GIFs.
Bugfix (3)
-----------
Under Pythonista, video production (animated gif only) did not start at the right time.
Fixed.
Internal changes (0)
--------------------
In order to support the new oneof parameter of request, the data structure of _requests and _claims is now
collections.OrderedDict instead of collections.defaultdict.
version 20.0.1 2020-02-27
==========================
New functionality (0)
---------------------
A new class ComponentGenerator is introduced.
With this, component can be generated according to a given inter arrival time (distribution) or a random spread
over a given interval.
Examples:
sim.ComponentGenerator(Car, iat=sim.Exponential(2))
# generates Cars according to a Poisson arrival
sim.ComponentGenerator(Car, iat=sim.Exponential(2), at=10, till=30, number=10)
# generates maximum 10 Cars according to a Poisson arrival from t=10 to t=30
sim.ComponentGenerator(Car, iat=sim.Exponential(2), at=10, till=30, number=10, force_at=True)
# generates maximum 10 Cars according to a Poisson arrival from t=10 to t=30, first arrival at t=10
sim.ComponentGenerator(sim.Pdf((Car, 0.7, Bus, 0.3)), iat=sim.Uniform(20,40), number=20)
# generates maximum 20 vehicles (70% Car, 30% Bus) according to a uniform inter arrival time
sim.ComponentGenerator(Car, duration=100, n=20)
# generates exactly 20 Cars, random spread over t=now and t=now+100
ComponentGenerator is a subclass of Component and therefore has all the normal properties and methods of an
ordinary component, altough it is not recommended to use any the process methods, apart from cancel.
Added functionality (0)
-----------------------
It is now possible to suppress line numbers in the trace.
Particularly when the trace output is written to a file, this can result in dramatic (up to 50 times!)
performance increase, because the calculation of line numbers is very demanding.
Now, therefore a method Environment.suppress_trace_linenumbers() has been introduced.
Like:
env = sim.Environment(trace=True)
env.suppress_trace_linenumbers(True)
By default, line numbers are not suppressed in the trace.
The current status can be queried with
print(env.suppress_trace_linenumbers())
version 20.0.0 2020-02-10
==========================
Announcement (0)
----------------
Salabim now runs fully also on Android platforms under the excellent Pydroid3 app
<https://play.google.com/store/apps/details?id=ru.iiec.pydroid3&hl=en>.
In order to use animation on that platform, it is required to import tkinter in the main program
, otherwise you will get a salabim error message if you try and start an animation.
With the PyDroid3 Premium version, your can also produce animation videos, albeit only .avi files.
AnimateButton does not properly yet and therefore the animation navigation buttons are not
placed correctly and in fact not usable. We plan a fix for this in a future version of salabim.
New functionality (0)
---------------------
An AnimateQueue object now has a queue() method that returns the queue it refers to.
This is useful in animation_objects() that want to check the queue (in the id parameter).
Improved functionality (0)
--------------------------
Normal video production (i.e. not .gif, .jpg, .bmp, .tiff, .png) is now done via a temporary file.
That has the effect that if the video production is not closed properly, the video is not written at all,
thus making the production process more stable.
It is necessary to always close the video production with
env.video("")
Changed functionality (0)
-------------------------
If a video name with extension .avi is to be produced (with env.video()), the default codec is now MJPG.
For all other extensions, the default codec remains MP4V.
Utility update (0)
------------------
The install.py utility is changed internally. Also, it shows now the location where salabim was installed.
Documentation (0)
-----------------
The width parameter of AnimateImage was not documented. Fixed.
Documentation (1)
-----------------
Closing a video is now documented (see also above).
Bug fix (0)
-----------
Minor bug in video production, when animation was turned off and on during the simulation. Fixed.
version 19.0.10 2019-12-18
===========================
New functionality (0)
---------------------
Map distribution allows sampled values from a distribution to be mapped to
a given function. E.g.
round_normal = sim.Map(sim.Normal(10, 4), lambda x: round(x))
or, equivalently:
round_normal = sim.Map(sim.Normal(10, 4), round)
The sampled values from the normal distribution will be rounded.
Another example:
positive_normal = sim.Map(sim.Normal(10, 4), lambda x: x if x > 0 else 0)
Negative sampled values will be set to zero, positive values are not affected.
Note that this is different from
sim.Bounded(sim.Normal(10, 4), lowerbound=0)
as in the latter case resampling will be done when a negative value is sampled,
in other words, virtually no zero samples.
New functionality (1)
---------------------
Introduced an AnimateCombined class with which it is possible to combine several instances of
AnimateText, AnimateCircle, AnimateRectangle, AnimatePolygon, AnimateLine, AnimatePoints,
AnimateImage or another AnimateCombined.
AnimateCombined works as a list, so can be filled by initializing with a list of Animatexxx instances, like
a = sim.AnimateCombined((a0, a1, a2))
, but can also be build up with
a.append(a3)
a.extend((a4, a5))
a += a6
etc.
It is then possible to set an attribute of the combined animation objects, like
a.x = 100
This will set the x for all animation objects to 100.
It is required that each of animation objects in an AnimatCombined instance supports the attribute
to be set! That means that you cannot set the radius for an AnimateCombined instance with an` AnimateCircle
and an AnimateRectangle, as the latter does not support radius.
If an attribute of an AnimateCombined instance is queried, the value for the first animation object will be returned.
It is possible to put AnimateCombined instances in another AnimateCombined object.
The remove method applied to AnimateCombined objects will remove all the included animation objects.
Changed functionality (0
--------------------------
In version 19.0.6 the following functionality was introduced:
If an error occurred in the make_pil_image method during an animation, it was sometimes difficult to
find out the reason for that error. From this version on, the line number (and the filename)
where the animation object was created will be added to the error message, which makes debugging
hopefully easier.
However, this feature sometimes appears to significantly slow down animations.
Therefore, this feature is now disabled by default.
If required (particularly when finding a complicated error), it is possible to enable the source location
tracking. The method Environment.animation_parameters() has therefore now a new parameter: animate_debug,
which is False by default.
Alternatively, the method Environment.animate_debug() can be used.
Changed functionality (1)
-------------------------
sim.Environment() now automatically resets the simulation environment when run under Pythonista.
A parameter 'do_reset' to sim.Environment() allows the user to force a reset as well, or to indicate
that no reset should be executed under Pythonista.
Internal change (0)
-------------------
The install.py file has been internally changed to support other single source
packages. No functional changes.
version 19.0.9 2019-10-08
==========================
New functionality (0)
---------------------
Introduced preemptive resources
It is now possible to specify that a resource is to be preemptive, by adding preemptive=True when the resource
is created.
If a component requests from a preemptive resource, it may bump component(s) that are claiming from
the resource, provided these have a lower priority = higher value).
If component is bumped, it releases the resource and is the activated, thus essentially stopping the current
action (usually hold or passivate).
Therefore, it is necessary that a component claiming from a preemptive resource should check
whether the component is bumped or still claiming at any point where they can be bumped.
This can be done with the method Component.isclaiming which is True if the component is claiming from the resource,
or the opposite (Component.isbumped) which is True is the component is not claiming from te resource.
E.g. if the component has to start all over again (hold(1)) if it is bumped:
def process(self):
prio = sim.Pdf((1,2,3), 1)
while True:
yield self.request((preemptive_resource, 1, prio)
yield self.hold(1)
if self.isclaiming(preemptive_resource):
break
self.release(preemptive_resource)
E.g. if the component just has to 'complete' the hold time:
def process(self):
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while True:
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
if self.isclaiming(preemptive_resource):
break
remaining -= (env.now() - self.mode_time())
self.release(preemptive_resource)
Note that a component that hasn't requested at all from a resource, it is considered as bumped, therefore the above
example can be rewritten as:
def process(self):
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while isbumped(preemptive_resource):
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
remaining -= (env.now() - self.modetime())
self.release(preemptive_resource)
Finally, if the component is dealing with just one preemptive resource (which is very likely), the isbumped()
and isclaiming() methods can be used without an argument:
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while isbumped():
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
remaining -= (env.now() - self.modetime())
self.release(preemptive_resource)
If a request for a preemptive resource is made, it is not possible to combine that request with any other
resource.
The method Resource.ispreemptive() can be used to check the whether a resource is preemptive.
There is an animated demo showing nicely the difference between preemptive and non preemptive resources
See salabim./sample models/Demo preemptive resources animated.py.
New functionality (1)
---------------------
In the trace, the time of rescheduling of Component initialization, activate, hold, request, wait and run
the time was always shown in the information field of the trace as 'scheduled for xxx'.
From this version, also the delta time (if not 0 of inf) is shown as +xxx behind the action, e.g.
75+ 1.000 main current
76 client.0 create
76 client.0 activate scheduled for 1.000 @ 59 process=process
77 client.1 create
77 client.1 activate +1.000 scheduled for 2.000 @ 59 process=process
78 client.2 create
78 client.2 activate +3.000 scheduled for 4.000 @ 59 process=process
New functionality (2)
---------------------
The trace parameter in Environment() and Environment.trace() can now be a file handle.
If so, the trace output will be directed to the file given, provided it is opened for output.
When the trace parameter is not an 'open for write' file handle, but is 'Truthy' (usually True),
the trace output is sent to stdout, as before.
When the parameter is 'Falsy', no trace output will be generated, as before.
Example:
import salabim as sim
class X(sim.Component):
pass
out = open('output.txt', 'w') as out:
env = sim.Environment(trace=out)
env.trace(True)
X()
env.trace(False)
X()
env.trace(out)
X()
env.trace(False)
out.close()
After execution, the file output.txt contains:
line# time current component action information
------ ---------- -------------------- ----------------------------------- -------------------------------
line numbers refers to test.py
8 default environment initialize
8 main create
8 0.000 main current
9 x.0 create data component
And the following output is generated:
12 x.1 create data component
Note that by issueing env.trace(False), the file is not closed, so that has to be done explicitely or with
a context manager.
Changed functionality (0)
-------------------------
The default priority of a requested resource is now inf, which means the lowest possible priority.
In practice, this will hardly ever make a difference with the former behaviour that if no priority was given, it was
added to the tail of the requesters
When a request is honoured, the component now enters the claimers queue with priority as it had in the requesters
queue. Again, in practice this will hardly make any difference.
Bugfix (0)
-----------
A bug with autonumbering components, queues, etc. by ending the name with a comma, like
for _ in range(3):
Car(name='car,')
fixed.
Now, the cars are correctly named car.1, car.2 and car.3 .
Bugfix (1)
-----------
On some platforms PIL does not accept a new image with a width or height of 0. Therefore, salabim now
sets the dimensions for a dummy image to (1, 1).
Bugfix (2)
-----------
As a leftover from a test, the seed was printed when an Environment was created. Removed.
version 19.0.8 2019-09-17
==========================
New functionality (0)
---------------------
The time related parameters of the methods methods mentioned below can now be called with a
distribution instead of a float value ('auto sampling ').
Method parameters that can be auto sampled
------------------------ -----------------------------------
sim.Component at, delay
Component.activate at, delay
Component.hold duration, till
Component.request fail_at, fail_delay
Component.wait fail_at, fail_delay
Environment.run duration, till
Environment.reset_now new_now
Environment.years, ... t
Environment.to_time_unit t
Environment.to_years, ... t
If a distribution is given, the distribution will be sampled.
So,
car = Car(delay=sim.Normal(100,10))
is equivalent to
car = Car(delay=sim.Normal(100,10).sample())
and
car = Car(delay=sim.Normal(100,10)())
This makes it possible to intermix floats/ints and distributions, without having to worry about sampling, e.g.
set_up_time = 6
processing_time = Normal(10, 1)
reaction_time = sim.Constant(10)
...
yield self.hold(set_up_time)
yield self.hold(processing_time)
yield self.hold(env.hours(reaction_time))
Note that salabim also supports basic expressions for distributions, so it is even possible to do something like
yield self.hold(2 * setup_time + processing_time)
New functionality (1)
---------------------
Introduced a new distribution, External, that makes it possible to specify an 'external' statistical
distribution from the modules
* random
* numpy.random
* scipy.stats
as were it a salabim distribution.
The class takes at least one parameter (dis) that specifies the distribution to use, like
* random.uniform
* numpy.random.uniform
* scipy.stats.uniform
Next, all postional and keyword parameters are used for sampling. In case of random and numpy.random by calling
the method itself, in case of a scipy.stats distribution by calling rvs().
Examples:
d = sim.External(random.lognormvariate, mu=5, sigma=1)
d = sim.External(numpy.random.laplace, loc=5, scale=1)
d = sim.SciPyDis(SciPy.beta, a=1, loc=4, scale=1)
Then sampling with
d.sample()
or
d()
If the size is given (only for numpy.random and scipy.stats distributions), salabim will return the sampled values successively. This can be useful to increase performance.
The mean() method returns the proper mean for scipy.stats distributions, nan otherwise.
If a time_unit parameter is given, the sampled values will be multiplied by the applicable factor, e.g.
env = sim.Environment(time_unit='seconds')
d = sim.ScyPyDis(SciPy.norm, loc=2, time_unit='minutes')
print(d.mean())
will print out
120
New functionality (2)
---------------------
Bounded distribution now has a time_unit parameter to specify the lowerbound or upperbound.
New functionality (3)
---------------------
IntUniform distribution now also supports a time_unit. If used, the returned value will be scaled accordingly.
E.g.
env = sim.Environment('seconds')
d = sim.IntUniform(1, 2, time_unit='minutes')
print(d.sample())
will print either 60 or 120.
Improvement (0)
---------------
When trying to animate text for a None object, salabim issued an error, about absence of .strip().
Now, None will be handled as the null string ("")
Improvement (1)
---------------
When a distribution with a time unit was specified without an Environment was instantiated, a rather obscure error
message was shown. Now, a clear error message will be shown, in that case.
Implementation note (0)
-----------------------
Provided numpy is installed, now numpy.random is seeded at init of Environment() and when calling random_seed,
unless the set_numpy_random_seed parameter is False.
This is particularly useful when using External distributions with
numpy.random or scypi.stats distributions. Therefore, unless explicitely overriden, also those
distribution sampling will be reproducable.
Related to this is an internal change in the way Environment seeds random.
Implementation note (1)
-----------------------
Change in the way time_units are handled internally.
version 19.0.7 2019-08-12
==========================
Bug fix (0)
-----------
Rare error in multiplication of monitors (and thus Monitor.to_hours, etc) fixed.
version 19.0.6 2019-07-09
==========================
Improved functionality (0)
--------------------------
AnimateMonitor and Monitor.animate() now allow rotation of an animated monitor.
This is particularly useful for animating a monitor from top to bottom (angle = 270),
instead of from left to right (angle=0)
The rotation angle is specified with the angle parameter, which defaults to 0.
Also, offsetx and offsety are now supported for AnimateMonitor and Monitor.animate().
Improved functionality (1)
--------------------------
The spec parameter of AnimateLine, AnimatePoints, AnimateRectangle and AnimatePolygon can now use None to repeat
a previous x or y-coordinate.
The same holds for the line0/line1, rectangle0/rectangle1, polygon0/polygon1 parameters of Animate and
Animate.update().
From now on it is therefore possible to say for instance:
sim.AnimateLine(spec=(100, 0, 900, None, None, 600))
which is equivalent to
sim.AnimateLine(spec=(100, 0, 900, 0, 900, 600))
And it even possible to say:
sim.AnimatePoints(spec=100, 400, 150, None, 200, None, 300, None)
which is equivalent to
sim.AnimatePoints(spec=100, 400, 150, 400, 200, 400, 300, 400)
Improved functionality (2)
--------------------------
Applications can now retrieve the salabim version consistenty with sim.__version__.
Previously, this had to be done with
- sim.__version__ if salabim.py is in the current directory
- sim.salabim.__version__ if salabim.py is not in the current directory
Techinal note: This change is actually in the __init__.py file rather than in salabim.py.
For comparisons of versions, distutils.version.StrictVersion() is recommended.
Improved functionality (3)
--------------------------
If an error occured in the make_pil_image method during an animation, it was sometimes difficult to
find out the reason for that error. From this version on, the line number (and the filename)
where the animation object was created will be added to the error message, which makes debugging
hopefully easier.
Bug fix (0)
-----------
Creating a video with audio did crash sometimes. Fixed.
Bug fix (1)
-----------
Under Pythonista (iOS), animation objects were not properly sorted, thus causing layer values sometimes to be
ignored. Fixed.
Bug fix (2)
-----------
When querying a tallied value from a level monitor, either with mon.get(t) or mon(t), the values were not correct
when a Environment.reset_time() was applied. Fixed.
version 19.0.5 2019-06-20
==========================
New functionality (0)
---------------------
Level monitors have two new properties:
- value, which can be used to get the current value of a monitor, i.e
a = m.value is equivalent to a = m.get() and a = m()
value can also be used to tally a value, i.e.
m.value = 10 is equivalent to m.tally(10)
Combining the getter and the setter is useful in constructs like
m.value += 100, which is equivalent to m.tally(m.get() + 100)
- t, which can be used to get the time the last value was tallied.
Combining these two is for instance useful to calculate the cost of storage over a period, such as
costs += (env.now() - inventory.t) * inventory.value * cost_per_day
inventory.value -= consume
Both value and t are available can be used even if the monitor is turned off.
Note that these two properties are only available for level monitors.
Note that this functionality is also available for the capacity of a resource, which means that increasing
the capacity of a resource by 1 can now be done with
res.set_capacity(res.capacity() + 1)
or
res.capacity.value += 1
The following standard salabim level monitors do support only *getting* the value and (for obvious reasons)
not *setting* the value:
- Queue.length()
- Resource.available_quantity()
- Resource.claimed_quantity()
- Resource.occupancy()
- State.value()
New functionality (1)
---------------------
Anonymous resources can now request for a negative quantity. In that case, the request gets honoured if
the claimed quantity is greater than or equal to minus the requested quantity.
In case of these anonymous resources, it might be easier to think in terms of get and put, like in
SimPy.Container. Therefore, salabim introduces to new methods:
- Component.get(), which is equivalent to Component.request
- Component.put(), which is equivalent to Component.request, but where the quantity of anonymous resources
is negated.
Thus yield self.request((r, -5)) is the same as yield self.put((r, 5)) if r is defined as
r = sim.Resource(name='r', anonymous=True)
Note that in the trace, the text 'request honor' will be shown even for get or request calls.
Also, isrequesting() will be True if a component is waiting for a get or put honor.
The Gas station model in sample models illustrates the get/put functionality.
Note that you can still 'refill' an anonymous resource without checking for exceeding the capacity, with a call to Resource.release(). In many cases that will be sufficient.
New functionality (2)
---------------------
On Windows and Pythonista platforms, an audio track (usually an mp3 file) may be played during animation.
This particularly during the development of lip synchronized videos.
Therefore, a new method Environment.audio() has been introduced.
Alternatively, the new parameter audio of Environment.animation_parameters() can be used to specify which
mp3 to be played.
It is recommended not to use variable bit rate (vbr) mp3 files, as the length can't be detected correctly.
Note that audio is only played when the animation speed is equal to audio_speed, which is 1 by default.
The audio_speed may be changed with Environment.audio_speed() or the new parameter audio_speed of
Environment.animation_parameters().
On other platforms than Windows or Pythonista, the audio functions are ignored.
The duration of an audio file (usually an mp3 file) can be retrieved with sim.audio_duration(filename).
On other platforms than Windows or Pythonista, a length of zero will be returned when sim.audio_duration() is called.
New functionality (3)
---------------------
On Windows platforms, an audio track can now be added to a video animation.
The audio is controlled by Environment.audio() commands (see above).
In order to add audio to a video, ffmpeg must be installed and included in the environment path.
See www.ffmpeg.org for download and installation instructions.
New functionality (4)
---------------------
When animating images, the alpha (transparency channel) can now be specified.
Therefore, Animation() and Animation.update() have two extra arguments: alpha0 and alpha1.
AnimateImage() has an extra parameter alpha.
The alpha value should be between 0 (fully transparent) and 255 (not transparent).
Note that if an image has alpha values other than 255, these are scaled accordingly.
Images with an alpha values of less than 255 are rendered quicker when numpy is installed.
Improved performance (0)
------------------------
Animation objects that are completely out of the frame are suppressed now. This results in better
performance when many animation objects are not visible anyway.
Improved performance (1)
------------------------
Rendering speed of animated text improved, particularly when numpy is installed.
Experimental functionality (0)
------------------------------
Experimental support for retina screens on supported iOS devices (Pythonista only).
By adding retina=True to the Environment() call, iOS devices will double the number of pixels, both
in height and width. As of now, buttons and sliders are not shown in retina mode.
Bug fixes (0)
-------------
In some Ubuntu environments, install.py could not install salabim correctly.
This has been fixed with this release.
version 19.0.4 2019-05-03
==========================
New functionality (0)
---------------------
Queue.extend() has an extra parameter, clear_source. If clear_source = True, the given source will be cleared
after copying the elements to self.
This means that
q0.extend(q1, clear_source=True)
effectively transfers all elements of q1 into q0, prior to emptying q1.
Note that clear_source cannot be applied if source is a list or a tuple.
New functionality (1)
---------------------
Non level monitors can now be filled from a list or tuple, like
m = Monitor("my monitor", level=False, fill=(1,2,5,7,2,3))
which is functionally equivalent to
m = Monitor("my monitor", level=False)
for el in (1,2,5,7,2,3):
m.tally(el)
Bug fix (0)
-----------
Autoscaling of histograms was incorrectly always enabled.
Now histograms are autoscaled only if neither number_of_bins, nor lowerbound nor bin_width is specified.
Bug fix (1)
-----------
Due to a problem with the Black formatter, version 19.0.3 did not run under Python versions prior to 3.6.
This is fixed with this release.
version 19.0.3 2019-04-21
==========================
New method (0)
--------------
Monitor.rename() can be used to rename a monitor in a chained way.
This is particularly useful when merging with the + operator, merging with sum or slicing with [],
multiplication and division as well as the unit conversion methods (to_years, ...),
because the resulting monitors get automatically a name, that might not be appropriate.
The Monitor.rename() method is essentially the same as Monitor.name(), but will return the monitor itself.
Examples:
(mon0 + mon1 + mon2 + mon3).rename('mon0 - mon3').print_histogram()
sum(mon for mon in (mon0, mon1, mon2, mon3)).rename('mon0 - mon3').print_histogram()
mon0[1000:2000].rename('mon0 between t=1000 and t=2000').print_statistics()
mon0.to_years().rename('mon0 in hours').print_statistics()
New method (1)
--------------
Queue.rename() can be used to rename a queue in a chained way.
This particularly useful when the queues are combined with +, -, |, & and ^ operator or the sum function,
because the resulting queue get automatically a name, that might not be appropriate.
The Queue.rename() method is essentially the same as Queue.name(), but will return the queue itself.
Examples:
(q0 + q1 + q2 + q3).rename('q0 - q3').print_statistics()
(q1 - q0).rename('difference of q1 and q0)').print_histograms()
New functionality
-----------------
It is now possible to get the union of several queues by means of the sum function.
Example:
rows = [Queue('row.') for _ in range(10)]
...
sum(rows).print_info()
Improved functionality (0)
--------------------------
When animating a large number of objects, it was possible that tkinter crashed because there were too many tkinter
bitmaps aka canvas objects, sometimes by issuing a 'Fail to allocate bitmap', sometimes without any message.
From this version on, salabim limits the number of bitmap automatically by combining animation objects
in one aggregated bitmap if the number of bitmaps exceeds a given maximum.
Unfortunately it is not possible to detect this 'Fail to allocate bitmap', so it may take some experimentation to
find a workable maximum (maybe going as low as 1000).
By default, salabim sets the maximum number of bitmaps to 4000, but may be changed with the
Environment.maximum_number_of_bitmaps() method, or the maximum_number_of_bitmaps parameter of
Environment.animation_parameters().
Choosing a too low maximum (particularly 0), may result in a performance degradation.
The bitmap aggregation process is transparent to the user, but improves the usability of salabim.
Note that does this not apply to the Pythonista implementation, where bitmaps are always aggregated.
Improved functionality (1)
--------------------------
When using the new style Animate classes (AnimateLine, AnimateRectangle, ...), texts are optional. Up to
this version, even a blank text (which is the default), resulted in a small 'empty' bitmap to be 'displayed'.
From this version, these blank texts are ignored automatically , which is transparent to the user but can result
in better performance and reduces the probability of a tkinter crash.
Changed functionality (0)
-------------------------
The method Environment.animation_parameters() does no longer automatically enable animate, but
instead leaves the animate status unchanged. So, if the user now wants to start the animation as
well when specifying other parameters, it is necessary to add animate=True.
Or, better yet, specify the various parameters with their corresponding method and use
env.animate(True).
E.g. instead of
env.animation_parameters(x0=100, modelname="My model")
use now
env.animation_parameters(x0=100, modelname="My model", animate=True)
or
env.x0(100)
env.modelname("My model")
env.animate(True)
Changed functionality (1)
-------------------------
When creating an animated video, the default codec is now mp4v instead of MP4V.
If this causes a problem in an appication, just add +MP4V to the filename, like
env.video("my_video.mp4+MP4V")
Changed functionality (2)
-------------------------
The function random_seed is defined in a slightly different way and is now in line with
the random_seed parameter of Environment().
The docstring and the documentation have been updated accordingly.
Bug fixes
---------
Minor error in AnimateMonitor and Monitor.animate() for non level monitors fixed.
version 19.0.2 2019-03-25
==========================
New functionality
-----------------
It is now possible to scale the output of non level monitor, which is most useful for the automatically
registered length_of_stay monitor of queues. For instance, if the time unit of the simulation is days,
the duration in the queue (q) is registered in days. Buf if a histogram in minutes is more appropriate,
it is possible to say
q.length_of_stay.to_hours().print_histogram()
Equivalent methods are available for year, weeks, days, minutes, seconds, milliseconds and microseconds.
Alternatively the method Monitor.to_time_unit() might be used as in
q.length_of_stay.to_time_unit('minutes').print_histogram()
Finally, a monitor may be scaled with a given factor, e.g.
q.length_of_stay.multiply(24 * 60).print_histogram()
or even
(q.length_of_stay * 24 * 60).print_histogram()
Here is a list of all the new Monitor methods:
Monitor.multiply()
Monitor.to_years()
Monitor.to_weeks()
Monitor.to_days()
Monitor.to_hours()
Monitor.to_minutes()
Monitor.to_seconds()
Monitor.to_milliseconds()
Monitor.to_microseconds()
Monitor.to_time_unit()
On top of Environment.to_years, Environment.to_weeks, etc., there is now a generic method
Environment.to_time_unit()
For instance,
env.to_minutes(env.now)
is equivalent to
env._to_time_unit('minutes', env.now)
Bug fixes
---------
Minor error in run without a duration or till parameter fixed (was not correctly fixed in version 19.0.1)
Bug in Queue.extend() fixed.
Bug in Queue.clear() fixed.
version 19.0.1 2019-03-02
=========================
Added functionality
-----------------
The methods Queue.add, Queue.append, Queue.add_at_head, Queue.add_sorted, Queue.add_in_front_of,
Queue.add_behind, Queue.insert, Queue.remove now return the the queue itself (self), in order
to allow chaining,like
waitingline.add(car1).add(car2)
Documentation update
--------------------
The documentation has been enhanced.
A section on using monitors in other packages, like matplotlib has been added.
This includes a hint to use
plt.plot(*waitingline.length.tx(), drawstyle="steps-post")
to generate a plot from a level monitor.
Bug fixes
---------
Minor error in run without a duration or till parameter fixed.
version 19.0.0 2019-01-01
=========================
New functionality
-----------------