Salabim

Latest version: v25.0.8

Safety actively analyzes 723400 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 15 of 28

19.0.3

==========================
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.

19.0.2

==========================

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.

19.0.1

=========================
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.

19.0.0

=========================
New functionality
-----------------
Queues now register the arrival rate and the departure rate, defined as
number of arrivals since last reset / duration since last reset
number of departures since last reset / duration since last reset
The following methods are available:
Queue.arrival_rate()
Queue.departure_rate()
The registration can be reset with
Queue.arrival_rate(reset=True)
Queue.departure_rate(reset=True)
Note that this functionality is completely independent of the monitoring.


Added functionality
-------------------
Video production now supports also the creation of a series of individual frames, in
.jpg, .png, .tiff or .bmp format.
By specifying video with one of these extension, the filename will be padded with 6 increasing digits, e.g.
env.video('test.jpg')
will write individual autonumbered frames named
test000000.jpg
test000001.jpg
test000002.jpg
...
Prior to creating the frames, all files matching the specification will be removed, in order to get only
the required frames, most likely for post processing with ffmpeg or similar.

Note that individual frame video production is available on all platforms, including Pythonista.


The method Environment.delete_video() can be used to delete all autonumbered files, like
env.delete_video('test.jpg')
will delete
test000000.jpg
test000001.jpg
test000002.jpg
...
If this method is used with any other file type, like .mp4, the file does not need to exist (i.e. no action is taken then).


Announcements
-------------
From this version, salabim uses the CalVer (see www.calver.org) standard for version numbering.
This means that versions are numbered YY.major.minor, where
YY is the year of the release minus 2000
major is the major version number (0 based)
minor is the minor version number (0 based)

From this version, legacy Python (<= 2.7) is not longer officially supported.

2.4.2

=========================
Added functionality
-------------------
Sampling from a Pdf distribution now also supports getting a number of samples without replacement.
In order to be able to use that, all probabilities have to be the same, like
colors_dis = sim.Pdf(("red", "green", "blue", "yellow"), 1)
Then we can get a random list of all colors with
colors_dis.sample(4) e.g. ["yellow", "green", "blue", "red"]
or two randomly choosen colors, without replacement with
colors_dis.sample(2) e.g. ["green", "blue"]
Note that if n=1, a list of one value will be returned
colors_dis,sample(1) e.g. ["blue"]


Bug fixes
---------
Under some conditions, Monitor.merge() did not work properly. Fixed.
Under rare conditions Pdf did not handle timeunits properly. Fixed.

Internal changes
----------------
The code is now 'blackened', causing neater and more consistent formatting.
This has not any effect on the functionality.

Sampling from a Pdf distribution has been optimized.

2.4.0

=========================
New Functionality
-----------------
Complete overhaul of Monitor/MonitorTimestamp internals, resulting in more consistent operation.
The documentation has been updated accordingly.
MonitorTimestamp is phased out and replaced by level monitors, which are instances of Monitor.
Timestamped monitors are now called level monitors, whereas non timestamped monitors are now called
non-level monitors.
non-level monitors now always timestamp the entry, which makes slicing and more useful animation possible.
It is no longer required (and possible) to indicate that a non-level monitor is weighted. If a weight is
required, just tally with a weight != 1.

Animation of non-level monitors is now also shown on a timescale, thus making it easier to see
the relation with a level monitor, e.g. length and length_of_stay of a queue.

This has a number of consequences for the API:

MonitorTimestamp ==> Monitor(level=True)
Merging of monitors not anymore part of the init, but is a separate method:
m = Monitor(merge=(m1, m2, m3)) ==> m = m1.merge(m2, m3)

It is no longer necessary (and possible) to specify weighted=True in init of Monitor.
All non-level monitors now support weighted, if required.
m = Monitor(weighted=True) ==> m = Monitor()

The (level monitor only) method .get() or direct call can now also be used to get the value of the level at a
specified time within the monitored period, e.g.
print('queue length at time 100 was', q.length.get(100))
or alternatively:
print('queue length at time 100 was', q.length(100))

It is now possible to slice a monitor with Monitor.slice(), which has two applications:
- to get statistics on a monitor with respect to a given time period, most likely a subrun
- to get statistics on a monitor with respect to a recurring time period, like hour 0-1, hour 0-2, etc.
Examples:
for i in range(10):
start = i * 1000
stop = (i+1) * 1000
print(f'mean length of q in [{start},{stop})={q.length.slice(start,stop).mean()}'
print(f'mean length of stay in [{start},{stop})={q.length_of_stay.slice(start,stop).mean()}'

for i in range(24):
print(f'mean length of q in hour {i}={q.length.slice(i, i+1, 24).mean()}'
print(f'mean length of stay of q in hour {i}={q.length_of_stay.slice(i, i+1, 24).mean()}'

Instead of slice(), a monitor can be sliced as well with the standard slice operator [], like:
q.length[1000:2000].print_histogram()
q.length[2:3:24].print_histogram()


It is now possible to change the format of times in trace, animation, etc. with the method
Environment.time_to_str_format()
For instance, if 5 decimals in the trace are to be shown instead of the default 3, issue:
env.time_to_str_format('{:10.5f}')
Make sure that the format represents 10 characters.
See the docstring / documentation for more information.


From now on it is possible, but definitely not required, to set the dimension of the time unit when defining an environment, like
env = sim.Environment(time_unit='hours')
Salabim supports 'years', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds' and 'n/a' as dimension.

If the time unit is set, times can be specified in any dimension, like
yield self.hold(env.seconds(20))
The following methods for conversion to the current time unit are available:
Environment.years()
Environment.weeks()
Environment.days()
Environment.hours()
Environment.minutes()
Environment.seconds()
Environment.milliseconds()
Environment.microseconds()
Example:
env = sim.Environment(time_unit='hours')
env.run(env.days(10))
This effectively let the simulation run for 240 (hours).

The following methods for conversion from time current time unit are available:
Environment.to_years()
Environment.to_weeks()
Environment.to_days()
Environment.to_hours()
Environment.to_minutes()
Environment.to_seconds()
Environment.to_milliseconds()
Environment.to_microseconds()
Example:
env = sim.Environment(time_unit='hours')
env.run(env.days(14))
print('it is now', env.to_days(env.now()), 'weeks')
output: it is now 2 weeks

Finally, the current time unit dimension can be queried with Environment.get_time_unit().
Example:
env = sim.Environment(time_unit='hours')
env.run(env.days(10))
print('it is now', env.now()), env.get_time_unit())
output: it is now 240 hours

All distributions, apart from IntUniform, Poisson and Beta now have an additional parameter, time_unit.
If the time_unit is specified at initialization of Environment(), the time_unit of the distribution
can now be specified.
As an example, suppose env has been initialized with env = sim.Environment(time_unit='hours').
If we then define a duration distribution as:
duration_dis = sim.Uniform(10, 20, 'days')
, the distribution is effectively uniform between 240 and 480 (hours).
This facility makes specification of duration distribution easier and more intuitive.
Refer to the docstrings or documentation for details.

By default the time unit dimension is 'n/a', which means that conversions are not possible.

The documentation has a new chapter Miscellaneous/Time units covering this topic.


Announcement
------------
The class PeriodMonitor that was added in version 2.3.4 will be phased out in a future release.
Slicing monitors (see above) is much more reliable and versatile.


Reminder
--------
Python 2.7 will no longer be supported as from 31 December 2018.
Please upgrade to Python 3.x as soon as possible.

Page 15 of 28

© 2025 Safety CLI Cybersecurity Inc. All Rights Reserved.