Salabim

Latest version: v25.0.8

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

Scan your dependencies

Page 13 of 28

20.0.4

==========================
New functionality (0)
---------------------
A component now supports a level monitor 'status' that keeps track of all the statuses a component has been in.
For instance, component.status.print_histogram(values=True) will print something like
Histogram of x.1.status
duration 40

value duration %
data 4.300 10.8 ********
interrupted 4 10 ********
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
scheduled 10.100 25.3 ********************

And of course, it is possible to get the duration a component was in a certain status, like
passive_time = component.status.value_duration(sim.passive).
You can even get the status of the component at a moment in the past, with e.g.
status_4_units_ago = c1.status(env.now() - 4)

In line with this change, the various statutes (passive, scheduled, ...) are no longer functions,
but just strings. So now, therefore it is also possible to say
passive_time = component.status.value_duration("passive")
This will be quite transparent from the user. Only if the text representation of a status was required,
status() had to be called, like
print(f"car status={car.status()()}")
From this version on, this should read
print(f"car status={car.status()}")
, which is also more intuitive.
Alternatively, you can now also write
print(f"car status={car.status.value}")

This makes the methods Component.ispassive(), Component.isscheduled, etc. less required as
component.status() == "passive" and
component.status() == "scheduled"
are arguably easier to understand.

The package salabim now has a function statuses() that returns a tuple with all statuses a component can be in.
So
print(sim.statuses())
will print
('current', 'data', 'interrupted', 'passive', 'requesting', 'scheduled', 'standby', 'waiting')

New functionality (1)
---------------------
Component.mode is now a monitor. That makes it possible to get an overview of all modes a component
has been in, either as a histogram or an animated monitor.
And it is possible to get the duration a component was in a certain mode, with e.g.
red_time = c1.mode.value_duration("red")
It is even possible to get the mode a component was in at a given moment, like
mode_4_units_ago = c1.mode(env.now() - 4)
Because of this you can't use mode(x) anymore to set the mode directly. In order to do that, you
have to use the new method set_mode:
c1.set_mode("green")
or
c1.mode.value = "green"
Both options do store the modetime correctly.
Please observe that the initial value for mode is now the null string and not None.

New_functionality (3)
---------------------
New method: Monitor.values() to assess all values in a monitor.
The values returned will be alphabetically sorted (not case sensitive), just as in Monitor.print_histogram().
The method supports both ex0 and force_numeric parameter flags.
Example (with same monitor as above):
print(x1.status.values())
will print
['data', 'interrupted', 'passive', 'requesting', 'scheduled']

New functionality (4)
---------------------
The values parameter of print_histogram() can now also be an iterable (i.e. tuple, list or set).
In that case, the statistics of only these values will be shown.
Example (with same monitor as above):
x1.status.print_histogram(values = sim.statuses())
will print
Histogram of x.1.status
duration 40

value duration %
current 0 0
data 4.300 10.8 ********
interrupted 4 10 ********
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
scheduled 10.100 25.3 ********************
standby 0 0
waiting 0 0

Any not shown values will be shown at the bottom as '<rest>'.
So, again with the same monitor as above:
x1.status.print_histogram(values = ("passive", "requesting", "error"))
will print
Histogram of x.1.status
duration 40

value duration %
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
error 0 0
<rest> 18.400 46.0 ************************************

New functionality (5)
---------------------
AnimateMonitor and Monitor.animate has got a new parameter: vertical_map.
This parameter should be a function that accepts one argument (the value to be plotted). By default
vertical_map is float.
The function vertical_map should result in a float or raise a TypeError or ValueError.
E.g. to map "red" to the value 1, "blue" to 2, etc., you could provide a mapping function like:
vertical_map = "unknown red blue green yellow".split().index
Note that in this example any value other than red, blue, green or yellow would map to 0
(via a ValueError exception).
This vertical_map function can also be used to map to a logarithmic scale:
vertical_map = lambda value_y: math.log(value_y) * 10

New functionality (6)
---------------------
AnimateMonitor and Monitor.animate can now show labels and corresponding lines.
There is full control of the colours, linewidth, fonts, placement, etc. See the docstring or
documentation for details.
There is a sample model (demo animation of labeled monitors.py) in the sample model folder
to illustrate the usage of this new functionality with the new Component.status and
Component.mode monitors.

Changed functionality (0)
-------------------------
The tests for which value(s) to include in
Montitor.value_number_of_entries()
Monitor.value_duration()
Monitor.value_weight()
are now direct and do not involve string conversion anymore. Normally, this won't cause any compatibility issues.

Distribution change (0)
-----------------------
The setup information (used in PyPI) now includes a set of classifiers as well as requires_python information.

Compatibility change (0)
------------------------
Salabim requires Python 3.6 from now on.

Internal change (0)
-------------------
The statuses requesting and waiting were internally stored as scheduled and 'translated' in the status()
method. Now, these statuses are natively stored as such (in the status monitor).

20.0.3

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

20.0.2

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

20.0.1

==========================
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())

20.0.0

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

19.0.10

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

Page 13 of 28

© 2025 Safety CLI Cybersecurity Inc. All Rights Reserved.