salabim 24.0.12__tar.gz → 24.0.14.post3__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {salabim-24.0.12 → salabim-24.0.14.post3}/PKG-INFO +1 -1
- {salabim-24.0.12 → salabim-24.0.14.post3}/pyproject.toml +1 -1
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/salabim.py +612 -60
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim.egg-info/PKG-INFO +1 -1
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_componentgenerator.py +24 -2
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_distributions.py +43 -2
- {salabim-24.0.12 → salabim-24.0.14.post3}/README.md +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/DejaVuSansMono.ttf +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/LICENSE.txt +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/__init__.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/calibri.ttf +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim/mplus-1m-regular.ttf +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim.egg-info/SOURCES.txt +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim.egg-info/dependency_links.txt +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/salabim.egg-info/top_level.txt +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/setup.cfg +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test salabim.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_cap_now.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_datetime.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_misc.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_monitor.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_process.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_queue.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_state.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_store.py +0 -0
- {salabim-24.0.12 → salabim-24.0.14.post3}/tests/test_timeunit.py +0 -0
@@ -8,7 +8,7 @@ authors = [
|
|
8
8
|
{name = "Ruud van der Ham", email = "rt.van.der.ham@gmail.com"}
|
9
9
|
]
|
10
10
|
description = "salabim - discrete event simulation in Python"
|
11
|
-
version = "24.0.
|
11
|
+
version = "24.0.14-3"
|
12
12
|
readme = "README.md"
|
13
13
|
requires-python = ">=3.7"
|
14
14
|
dependencies = [
|
@@ -1,13 +1,13 @@
|
|
1
|
-
# _ _ _ ____ _ _ ___ _
|
2
|
-
# ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | || | / _ \ / ||
|
3
|
-
# / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) || || |_ | | | | |
|
4
|
-
# \__ \| (_| || || (_| || |_) || || | | | | | / __/ |__ _| _ | |_| | _ | |
|
5
|
-
# |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____| |_| (_) \___/ (_)|_
|
1
|
+
# _ _ _ ____ _ _ ___ _ _ _
|
2
|
+
# ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | || | / _ \ / || || |
|
3
|
+
# / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) || || |_ | | | | | || || |_
|
4
|
+
# \__ \| (_| || || (_| || |_) || || | | | | | / __/ |__ _| _ | |_| | _ | ||__ _|
|
5
|
+
# |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____| |_| (_) \___/ (_)|_| |_|
|
6
6
|
# discrete event simulation
|
7
7
|
#
|
8
8
|
# see www.salabim.org for more information, the documentation and license information
|
9
9
|
|
10
|
-
__version__ = "24.0.
|
10
|
+
__version__ = "24.0.14"
|
11
11
|
|
12
12
|
import heapq
|
13
13
|
import random
|
@@ -60,7 +60,7 @@ PyDroid = sys.platform == "linux" and any("pydroid" in v for v in os.environ.val
|
|
60
60
|
PyPy = platform.python_implementation() == "PyPy"
|
61
61
|
Chromebook = "penguin" in platform.uname()
|
62
62
|
PythonInExcel = not ("__file__" in globals())
|
63
|
-
AnacondaCode = sys.platform=="emscripten"
|
63
|
+
AnacondaCode = sys.platform == "emscripten"
|
64
64
|
|
65
65
|
|
66
66
|
def a_log(*args):
|
@@ -154,8 +154,9 @@ nan = float("nan")
|
|
154
154
|
if Pythonista or AnacondaCode or PythonInExcel:
|
155
155
|
_yieldless = False
|
156
156
|
else:
|
157
|
-
_yieldless=True
|
158
|
-
|
157
|
+
_yieldless = True
|
158
|
+
|
159
|
+
|
159
160
|
class QueueFullError(Exception):
|
160
161
|
pass
|
161
162
|
|
@@ -4144,7 +4145,7 @@ if Pythonista:
|
|
4144
4145
|
try:
|
4145
4146
|
ims = scene.load_pil_image(capture_image)
|
4146
4147
|
except SystemError:
|
4147
|
-
im_file = "temp.png" # hack for Pythonista 3.4
|
4148
|
+
im_file = "temp.png" # hack for Pythonista 3.4
|
4148
4149
|
capture_image.save(im_file, "PNG")
|
4149
4150
|
ims = scene.load_image_file(im_file)
|
4150
4151
|
scene.image(ims, 0, 0, *capture_image.size)
|
@@ -5717,7 +5718,7 @@ class Queue:
|
|
5717
5718
|
|
5718
5719
|
class Store(Queue):
|
5719
5720
|
def __init__(self, name: str = None, monitor: Any = True, fill: Iterable = None, capacity: float = inf, env: "Environment" = None, *args, **kwargs) -> None:
|
5720
|
-
super().__init__(name=name, monitor=monitor,fill=None,capacity=capacity, env=env, *args, **kwargs)
|
5721
|
+
super().__init__(name=name, monitor=monitor, fill=None, capacity=capacity, env=env, *args, **kwargs)
|
5721
5722
|
|
5722
5723
|
with self.env.suppress_trace():
|
5723
5724
|
self._to_store_requesters = Queue(f"{name}.to_store_requesters", env=env)
|
@@ -7981,6 +7982,10 @@ by adding:
|
|
7981
7982
|
----
|
7982
7983
|
Only if yieldless is False: if to be used for the current component, use ``yield self.cancel()``.
|
7983
7984
|
"""
|
7985
|
+
if self.status.value == data:
|
7986
|
+
if self.env._trace:
|
7987
|
+
self.env.print_trace("", "", "cancel (on data component) " + self.name() + " " + self._modetxt())
|
7988
|
+
return
|
7984
7989
|
if self.status.value != current:
|
7985
7990
|
self._checkisnotdata()
|
7986
7991
|
self._remove()
|
@@ -9048,10 +9053,7 @@ by adding:
|
|
9048
9053
|
self.status._value = waiting
|
9049
9054
|
self._reschedule(scheduled_time, schedule_priority, urgent, "wait", cap_now)
|
9050
9055
|
else:
|
9051
|
-
return
|
9052
|
-
if self.env._yieldless:
|
9053
|
-
if self is self.env._current_component:
|
9054
|
-
self.env._glet.switch()
|
9056
|
+
return
|
9055
9057
|
|
9056
9058
|
def _trywait(self):
|
9057
9059
|
if self.status.value == interrupted:
|
@@ -10000,6 +10002,211 @@ by adding:
|
|
10000
10002
|
return None
|
10001
10003
|
|
10002
10004
|
|
10005
|
+
class Event(Component):
|
10006
|
+
"""
|
10007
|
+
Event object
|
10008
|
+
|
10009
|
+
An event object is a specialized Component that is usually not subclassed.
|
10010
|
+
|
10011
|
+
Apart from the usual Component parameters it has an action parameter, to specifies what should
|
10012
|
+
happen after becoming active. This action is usually a lambda function.
|
10013
|
+
|
10014
|
+
Parameters
|
10015
|
+
----------
|
10016
|
+
action : callable
|
10017
|
+
function called when the component becomes current.
|
10018
|
+
|
10019
|
+
action_string : str
|
10020
|
+
string to be printed in trace when action gets executed (default: "action")
|
10021
|
+
|
10022
|
+
name : str
|
10023
|
+
name of the component.
|
10024
|
+
|
10025
|
+
if the name ends with a period (.),
|
10026
|
+
auto serializing will be applied
|
10027
|
+
|
10028
|
+
if the name end with a comma,
|
10029
|
+
auto serializing starting at 1 will be applied
|
10030
|
+
|
10031
|
+
if omitted, the name will be derived from the class
|
10032
|
+
it is defined in (lowercased)
|
10033
|
+
|
10034
|
+
at : float or distribution
|
10035
|
+
schedule time
|
10036
|
+
|
10037
|
+
if omitted, now is used
|
10038
|
+
|
10039
|
+
if distribution, the distribution is sampled
|
10040
|
+
|
10041
|
+
delay : float or distributiom
|
10042
|
+
schedule with a delay
|
10043
|
+
|
10044
|
+
if omitted, no delay
|
10045
|
+
|
10046
|
+
if distribution, the distribution is sampled
|
10047
|
+
|
10048
|
+
priority : float
|
10049
|
+
priority
|
10050
|
+
|
10051
|
+
default: 0
|
10052
|
+
|
10053
|
+
if a component has the same time on the event list, this component is sorted accoring to
|
10054
|
+
the priority.
|
10055
|
+
|
10056
|
+
urgent : bool
|
10057
|
+
urgency indicator
|
10058
|
+
|
10059
|
+
if False (default), the component will be scheduled
|
10060
|
+
behind all other components scheduled
|
10061
|
+
for the same time and priority
|
10062
|
+
|
10063
|
+
if True, the component will be scheduled
|
10064
|
+
in front of all components scheduled
|
10065
|
+
for the same time and priority
|
10066
|
+
|
10067
|
+
suppress_trace : bool
|
10068
|
+
suppress_trace indicator
|
10069
|
+
|
10070
|
+
if True, this component will be excluded from the trace
|
10071
|
+
|
10072
|
+
If False (default), the component will be traced
|
10073
|
+
|
10074
|
+
Can be queried or set later with the suppress_trace method.
|
10075
|
+
|
10076
|
+
suppress_pause_at_step : bool
|
10077
|
+
suppress_pause_at_step indicator
|
10078
|
+
|
10079
|
+
if True, if this component becomes current, do not pause when stepping
|
10080
|
+
|
10081
|
+
If False (default), the component will be paused when stepping
|
10082
|
+
|
10083
|
+
Can be queried or set later with the suppress_pause_at_step method.
|
10084
|
+
|
10085
|
+
skip_standby : bool
|
10086
|
+
skip_standby indicator
|
10087
|
+
|
10088
|
+
if True, after this component became current, do not activate standby components
|
10089
|
+
|
10090
|
+
If False (default), after the component became current activate standby components
|
10091
|
+
|
10092
|
+
Can be queried or set later with the skip_standby method.
|
10093
|
+
|
10094
|
+
mode : str preferred
|
10095
|
+
mode
|
10096
|
+
|
10097
|
+
will be used in trace and can be used in animations
|
10098
|
+
|
10099
|
+
if omitted, the mode will be "".
|
10100
|
+
|
10101
|
+
also mode_time will be set to now.
|
10102
|
+
|
10103
|
+
cap_now : bool
|
10104
|
+
indicator whether times (at, delay) in the past are allowed. If, so now() will be used.
|
10105
|
+
default: sys.default_cap_now(), usualy False
|
10106
|
+
|
10107
|
+
env : Environment
|
10108
|
+
environment where the component is defined
|
10109
|
+
|
10110
|
+
if omitted, default_env will be used
|
10111
|
+
"""
|
10112
|
+
|
10113
|
+
def __init__(
|
10114
|
+
self,
|
10115
|
+
action: Callable,
|
10116
|
+
action_string="action",
|
10117
|
+
name: str = None,
|
10118
|
+
at: Union[float, Callable] = None,
|
10119
|
+
delay: Union[float, Callable] = None,
|
10120
|
+
priority: float = None,
|
10121
|
+
urgent: bool = None,
|
10122
|
+
suppress_trace: bool = False,
|
10123
|
+
suppress_pause_at_step: bool = False,
|
10124
|
+
skip_standby: bool = False,
|
10125
|
+
mode: str = "",
|
10126
|
+
cap_now: bool = None,
|
10127
|
+
env: "Environment" = None,
|
10128
|
+
**kwargs,
|
10129
|
+
):
|
10130
|
+
self._action = action
|
10131
|
+
self._action_string = action_string
|
10132
|
+
self._action_taken = False
|
10133
|
+
if env is None:
|
10134
|
+
env = g.default_env
|
10135
|
+
super().__init__(
|
10136
|
+
name=name,
|
10137
|
+
at=at,
|
10138
|
+
delay=delay,
|
10139
|
+
priority=priority,
|
10140
|
+
urgent=urgent,
|
10141
|
+
suppress_trace=suppress_trace,
|
10142
|
+
suppress_pause_at_step=suppress_pause_at_step,
|
10143
|
+
skip_standby=skip_standby,
|
10144
|
+
mode=mode,
|
10145
|
+
cap_now=cap_now,
|
10146
|
+
env=env,
|
10147
|
+
process="process" if env._yieldless else "process_yield",
|
10148
|
+
**kwargs,
|
10149
|
+
)
|
10150
|
+
|
10151
|
+
def process_yield(self):
|
10152
|
+
self.env.print_trace("", "", self._action_string, "")
|
10153
|
+
self._action()
|
10154
|
+
self._action_taken = True
|
10155
|
+
return
|
10156
|
+
yield 1 # just to make it a generator
|
10157
|
+
|
10158
|
+
def process(self):
|
10159
|
+
self.env.print_trace("", "", self._action_string, "")
|
10160
|
+
self._action()
|
10161
|
+
self._action_taken = True
|
10162
|
+
|
10163
|
+
def action(self, value=None):
|
10164
|
+
"""
|
10165
|
+
action
|
10166
|
+
|
10167
|
+
Parameters
|
10168
|
+
----------
|
10169
|
+
value : callable
|
10170
|
+
new action callable
|
10171
|
+
|
10172
|
+
Returns
|
10173
|
+
-------
|
10174
|
+
current action : callable
|
10175
|
+
"""
|
10176
|
+
if value is not None:
|
10177
|
+
self._action = value
|
10178
|
+
return self._action
|
10179
|
+
|
10180
|
+
def action_string(self, value=None):
|
10181
|
+
"""
|
10182
|
+
action_string
|
10183
|
+
|
10184
|
+
Parameters
|
10185
|
+
----------
|
10186
|
+
value : string
|
10187
|
+
new action_string
|
10188
|
+
|
10189
|
+
Returns
|
10190
|
+
-------
|
10191
|
+
current action_string : string
|
10192
|
+
"""
|
10193
|
+
|
10194
|
+
if value is not None:
|
10195
|
+
self._action_string = value
|
10196
|
+
return self._action_string
|
10197
|
+
|
10198
|
+
def action_taken(self):
|
10199
|
+
"""
|
10200
|
+
action_taken
|
10201
|
+
|
10202
|
+
Returns
|
10203
|
+
-------
|
10204
|
+
action taken: bool
|
10205
|
+
True if action has been taken, False if not
|
10206
|
+
"""
|
10207
|
+
return self._action_taken
|
10208
|
+
|
10209
|
+
|
10003
10210
|
class Environment:
|
10004
10211
|
"""
|
10005
10212
|
environment object
|
@@ -10791,7 +10998,7 @@ class Environment:
|
|
10791
10998
|
if c.overridden_lineno:
|
10792
10999
|
self.print_trace(self.time_to_str(self._now - self._offset), c.name(), "current", s0=un_na(c.overridden_lineno))
|
10793
11000
|
else:
|
10794
|
-
self.print_trace(self.time_to_str(self._now - self._offset), c.name(), "current", s0=un_na(c.lineno_txt()))
|
11001
|
+
self.print_trace(self.time_to_str(self._now - self._offset), c.name(), "current", s0=un_na(c.lineno_txt()))
|
10795
11002
|
if c == self._main:
|
10796
11003
|
self.running = False
|
10797
11004
|
return
|
@@ -11141,13 +11348,13 @@ class Environment:
|
|
11141
11348
|
|
11142
11349
|
if width is not None:
|
11143
11350
|
if self._width != width:
|
11144
|
-
self._width = width
|
11351
|
+
self._width = int(width)
|
11145
11352
|
frame_changed = True
|
11146
11353
|
width_changed = True
|
11147
11354
|
|
11148
11355
|
if height is not None:
|
11149
11356
|
if self._height != height:
|
11150
|
-
self._height = height
|
11357
|
+
self._height = int(height)
|
11151
11358
|
frame_changed = True
|
11152
11359
|
height_changed = True
|
11153
11360
|
|
@@ -11506,7 +11713,7 @@ class Environment:
|
|
11506
11713
|
if self._video_pingpong:
|
11507
11714
|
self._images.extend(self._images[::-1])
|
11508
11715
|
if self._video_repeat == 1: # in case of repeat == 1, loop should not be specified (otherwise, it might show twice)
|
11509
|
-
if PythonInExcel or AnacondaCode:
|
11716
|
+
if PythonInExcel or AnacondaCode:
|
11510
11717
|
with b64_file_handler(self._video_name, mode="b", result=_pie_result) as f:
|
11511
11718
|
self._images[0].save(
|
11512
11719
|
f,
|
@@ -11527,7 +11734,7 @@ class Environment:
|
|
11527
11734
|
optimize=False,
|
11528
11735
|
)
|
11529
11736
|
else:
|
11530
|
-
if PythonInExcel or AnacondaCode:
|
11737
|
+
if PythonInExcel or AnacondaCode:
|
11531
11738
|
with b64_file_handler(self._video_name, mode="b", result=_pie_result) as f:
|
11532
11739
|
self._images[0].save(
|
11533
11740
|
f,
|
@@ -11596,7 +11803,8 @@ class Environment:
|
|
11596
11803
|
for ao in an_objects:
|
11597
11804
|
ao.make_pil_image(self.t())
|
11598
11805
|
if ao._image_visible and (include_topleft or not ao.getattr("in_topleft", False)):
|
11599
|
-
image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])),
|
11806
|
+
image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])),ao._image.convert("RGBA"),)
|
11807
|
+
|
11600
11808
|
|
11601
11809
|
return image.convert(mode)
|
11602
11810
|
|
@@ -12993,7 +13201,7 @@ class Environment:
|
|
12993
13201
|
if co is None:
|
12994
13202
|
if len(g.canvas_objects) >= self._maximum_number_of_bitmaps:
|
12995
13203
|
if overflow_image is None:
|
12996
|
-
overflow_image = Image.new("RGBA", (self._width, self._height), (0, 0, 0, 0))
|
13204
|
+
overflow_image = Image.new("RGBA", (int(self._width), int(self._height)), (0, 0, 0, 0))
|
12997
13205
|
overflow_image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])), ao._image)
|
12998
13206
|
ao.canvas_object = None
|
12999
13207
|
else:
|
@@ -15218,10 +15426,20 @@ class Animate2dBase(DynamicClass):
|
|
15218
15426
|
spec = self.image(t)
|
15219
15427
|
image_container = ImageContainer(spec)
|
15220
15428
|
width = self.width(t)
|
15429
|
+
height = self.height(t)
|
15430
|
+
|
15221
15431
|
if width is None:
|
15222
|
-
|
15432
|
+
if height is None:
|
15433
|
+
width = image_container.images[0].size[0]
|
15434
|
+
height = image_container.images[0].size[1]
|
15435
|
+
else:
|
15436
|
+
width = height * image_container.images[0].size[0] / image_container.images[0].size[1]
|
15437
|
+
else:
|
15438
|
+
if height is None:
|
15439
|
+
height = width * image_container.images[0].size[1] / image_container.images[0].size[0]
|
15440
|
+
else:
|
15441
|
+
...
|
15223
15442
|
|
15224
|
-
height = width * image_container.images[0].size[1] / image_container.images[0].size[0]
|
15225
15443
|
if not self.screen_coordinates:
|
15226
15444
|
width *= self.env._scale
|
15227
15445
|
height *= self.env._scale
|
@@ -15240,7 +15458,6 @@ class Animate2dBase(DynamicClass):
|
|
15240
15458
|
offsety = offsety * self.env._scale
|
15241
15459
|
|
15242
15460
|
alpha = int(self.alpha(t))
|
15243
|
-
|
15244
15461
|
image, id = image_container.get_image(
|
15245
15462
|
(t - self.animation_start(t)) * self.animation_speed(t),
|
15246
15463
|
repeat=self.animation_repeat(t),
|
@@ -15248,6 +15465,7 @@ class Animate2dBase(DynamicClass):
|
|
15248
15465
|
t_from=self.animation_from(t),
|
15249
15466
|
t_to=self.animation_to(t),
|
15250
15467
|
)
|
15468
|
+
|
15251
15469
|
self._image_ident = (spec, id, width, height, angle, alpha, flip_horizontal, flip_vertical)
|
15252
15470
|
|
15253
15471
|
if self._image_ident != self._image_ident_prev:
|
@@ -15312,8 +15530,6 @@ class Animate2dBase(DynamicClass):
|
|
15312
15530
|
|
15313
15531
|
if not self.screen_coordinates:
|
15314
15532
|
fontsize = fontsize * self.env._scale
|
15315
|
-
# offsetx = offsetx * self.env._scale # ***
|
15316
|
-
# offsety = offsety * self.env._scale # ***
|
15317
15533
|
text_anchor = self.text_anchor(t)
|
15318
15534
|
|
15319
15535
|
if self.attached_to:
|
@@ -15347,6 +15563,7 @@ class Animate2dBase(DynamicClass):
|
|
15347
15563
|
qx = (x - self.env._x0) * self.env._scale
|
15348
15564
|
qy = (y - self.env._y0) * self.env._scale
|
15349
15565
|
max_lines = self.max_lines(t)
|
15566
|
+
|
15350
15567
|
self._image_ident = (text, fontname, fontsize, angle, textcolor, max_lines)
|
15351
15568
|
if self._image_ident != self._image_ident_prev:
|
15352
15569
|
font, heightA = getfont(fontname, fontsize)
|
@@ -15529,6 +15746,9 @@ class AnimateClassic(Animate2dBase):
|
|
15529
15746
|
def width(self, t):
|
15530
15747
|
return self.master.width(t)
|
15531
15748
|
|
15749
|
+
def height(self, t):
|
15750
|
+
return self.master.height(t)
|
15751
|
+
|
15532
15752
|
def anchor(self, t):
|
15533
15753
|
return self.master.anchor(t)
|
15534
15754
|
|
@@ -15756,6 +15976,11 @@ class Animate:
|
|
15756
15976
|
|
15757
15977
|
if omitted or None, no scaling
|
15758
15978
|
|
15979
|
+
height0 : float
|
15980
|
+
width of the image to be displayed at time t0
|
15981
|
+
|
15982
|
+
if omitted or None, no scaling
|
15983
|
+
|
15759
15984
|
t1 : float
|
15760
15985
|
time of end of the animation (default inf)
|
15761
15986
|
|
@@ -15824,6 +16049,9 @@ class Animate:
|
|
15824
16049
|
width1 : float
|
15825
16050
|
width of the image to be displayed at time t1 (default: width0)
|
15826
16051
|
|
16052
|
+
height1 : float
|
16053
|
+
width of the image to be displayed at time t1 (default: height0)
|
16054
|
+
|
15827
16055
|
over3d : bool
|
15828
16056
|
if True, this object will be rendered to the OpenGL window
|
15829
16057
|
|
@@ -15892,6 +16120,7 @@ class Animate:
|
|
15892
16120
|
font -
|
15893
16121
|
fontsize0,fontsize1 -
|
15894
16122
|
width0,width1 -
|
16123
|
+
height0,height1 -
|
15895
16124
|
====================== ========= ========= ========= ========= ========= =========
|
15896
16125
|
"""
|
15897
16126
|
|
@@ -15927,6 +16156,7 @@ class Animate:
|
|
15927
16156
|
alpha0: float = 255,
|
15928
16157
|
fontsize0: float = 20,
|
15929
16158
|
width0: float = None,
|
16159
|
+
height0: float = None,
|
15930
16160
|
t1: float = None,
|
15931
16161
|
x1: float = None,
|
15932
16162
|
y1: float = None,
|
@@ -15945,6 +16175,7 @@ class Animate:
|
|
15945
16175
|
alpha1: float = None,
|
15946
16176
|
fontsize1: float = None,
|
15947
16177
|
width1: float = None,
|
16178
|
+
height1: float = None,
|
15948
16179
|
xy_anchor: str = "",
|
15949
16180
|
over3d: bool = None,
|
15950
16181
|
flip_horizontal: bool = False,
|
@@ -15991,10 +16222,12 @@ class Animate:
|
|
15991
16222
|
self.text0 = text
|
15992
16223
|
|
15993
16224
|
if image is None:
|
15994
|
-
self.width0 = 0 # just to be able to
|
16225
|
+
self.width0 = 0 # just to be able to interpolat
|
16226
|
+
self.height0 = 0
|
15995
16227
|
else:
|
15996
16228
|
self.image0 = image
|
15997
16229
|
self.width0 = width0 # None means original size
|
16230
|
+
self.height0 = height0
|
15998
16231
|
|
15999
16232
|
self.as_points0 = as_points
|
16000
16233
|
self.font0 = font
|
@@ -16056,7 +16289,7 @@ class Animate:
|
|
16056
16289
|
self.alpha1 = self.alpha0 if alpha1 is None else alpha1
|
16057
16290
|
self.fontsize1 = self.fontsize0 if fontsize1 is None else fontsize1
|
16058
16291
|
self.width1 = self.width0 if width1 is None else width1
|
16059
|
-
|
16292
|
+
self.height1 = self.height0 if height1 is None else height1
|
16060
16293
|
self.t1 = inf if t1 is None else t1
|
16061
16294
|
if self.env._animate_debug:
|
16062
16295
|
self.caller = self.env._frame_to_lineno(_get_caller_frame(), add_filename=True)
|
@@ -16115,6 +16348,7 @@ class Animate:
|
|
16115
16348
|
alpha0=None,
|
16116
16349
|
fontsize0=None,
|
16117
16350
|
width0=None,
|
16351
|
+
height0=None,
|
16118
16352
|
xy_anchor1=None,
|
16119
16353
|
as_points=None,
|
16120
16354
|
t1=None,
|
@@ -16135,6 +16369,7 @@ class Animate:
|
|
16135
16369
|
alpha1=None,
|
16136
16370
|
fontsize1=None,
|
16137
16371
|
width1=None,
|
16372
|
+
height1=None,
|
16138
16373
|
flip_horizontal=None,
|
16139
16374
|
flip_vertical=None,
|
16140
16375
|
animation_start=None,
|
@@ -16287,6 +16522,11 @@ class Animate:
|
|
16287
16522
|
|
16288
16523
|
if None, the original width of the image will be used
|
16289
16524
|
|
16525
|
+
height0 : float
|
16526
|
+
height of the image to be displayed at time t0 (default see below)
|
16527
|
+
|
16528
|
+
if None, the original height of the image will be used
|
16529
|
+
|
16290
16530
|
t1 : float
|
16291
16531
|
time of end of the animation (default: inf)
|
16292
16532
|
|
@@ -16353,6 +16593,8 @@ class Animate:
|
|
16353
16593
|
width1 : float
|
16354
16594
|
width of the image to be displayed at time t1 (default: width0)
|
16355
16595
|
|
16596
|
+
height1 : float
|
16597
|
+
height of the image to be displayed at time t1 (default: height0)
|
16356
16598
|
|
16357
16599
|
Note
|
16358
16600
|
----
|
@@ -16388,6 +16630,8 @@ class Animate:
|
|
16388
16630
|
self.max_lines0 = max_lines
|
16389
16631
|
|
16390
16632
|
self.width0 = self.width() if width0 is None else width0
|
16633
|
+
self.height0 = self.height() if height0 is None else height0
|
16634
|
+
|
16391
16635
|
if image is not None:
|
16392
16636
|
self.image0 = image
|
16393
16637
|
|
@@ -16434,6 +16678,7 @@ class Animate:
|
|
16434
16678
|
self.alpha1 = self.alpha0 if alpha1 is None else alpha1
|
16435
16679
|
self.fontsize1 = self.fontsize0 if fontsize1 is None else fontsize1
|
16436
16680
|
self.width1 = self.width0 if width1 is None else width1
|
16681
|
+
self.height1 = self.height0 if height1 is None else height1
|
16437
16682
|
self.xy_anchor1 = self.xy_anchor0 if xy_anchor1 is None else xy_anchor1
|
16438
16683
|
|
16439
16684
|
self.t1 = inf if t1 is None else t1
|
@@ -16731,7 +16976,7 @@ class Animate:
|
|
16731
16976
|
|
16732
16977
|
def width(self, t=None):
|
16733
16978
|
"""
|
16734
|
-
width
|
16979
|
+
width of an animated image object. May be overridden.
|
16735
16980
|
|
16736
16981
|
Parameters
|
16737
16982
|
----------
|
@@ -16756,6 +17001,33 @@ class Animate:
|
|
16756
17001
|
|
16757
17002
|
return interpolate((self.env._now if t is None else t), self.t0, self.t1, width0, width1)
|
16758
17003
|
|
17004
|
+
def height(self, t=None):
|
17005
|
+
"""
|
17006
|
+
height of an animated image object. May be overridden.
|
17007
|
+
|
17008
|
+
Parameters
|
17009
|
+
----------
|
17010
|
+
t : float
|
17011
|
+
current time
|
17012
|
+
|
17013
|
+
Returns
|
17014
|
+
-------
|
17015
|
+
height : float
|
17016
|
+
default behaviour: linear interpolation between self.height0 and self.height1
|
17017
|
+
|
17018
|
+
if None, the original height of the image will be used
|
17019
|
+
"""
|
17020
|
+
height0 = self.height0
|
17021
|
+
height1 = self.height1
|
17022
|
+
if height0 is None and height1 is None:
|
17023
|
+
return None
|
17024
|
+
if height0 is None:
|
17025
|
+
height0 = ImageContainer(self.image0).images[0].size[0]
|
17026
|
+
if height1 is None:
|
17027
|
+
height1 = ImageContainer(self.image1).images[0].size[0]
|
17028
|
+
|
17029
|
+
return interpolate((self.env._now if t is None else t), self.t0, self.t1, height0, height1)
|
17030
|
+
|
16759
17031
|
def fontsize(self, t=None):
|
16760
17032
|
"""
|
16761
17033
|
fontsize of an animate object. May be overridden.
|
@@ -19449,6 +19721,8 @@ class AnimateImage(Animate2dBase):
|
|
19449
19721
|
width : float
|
19450
19722
|
width of the image (default: None = no scaling)
|
19451
19723
|
|
19724
|
+
heighth : float
|
19725
|
+
height of the image (default: None = no scaling)
|
19452
19726
|
|
19453
19727
|
text : str, tuple or list
|
19454
19728
|
the text to be displayed
|
@@ -19584,6 +19858,7 @@ class AnimateImage(Animate2dBase):
|
|
19584
19858
|
x: Union[float, Callable] = None,
|
19585
19859
|
y: Union[float, Callable] = None,
|
19586
19860
|
width: Union[float, Callable] = None,
|
19861
|
+
height: Union[float, Callable] = None,
|
19587
19862
|
text: Union[str, Callable] = None,
|
19588
19863
|
fontsize: Union[float, Callable] = None,
|
19589
19864
|
textcolor: Union[ColorType, Callable] = None,
|
@@ -19626,6 +19901,7 @@ class AnimateImage(Animate2dBase):
|
|
19626
19901
|
x=0,
|
19627
19902
|
y=0,
|
19628
19903
|
width=None,
|
19904
|
+
height=None,
|
19629
19905
|
text="",
|
19630
19906
|
fontsize=15,
|
19631
19907
|
textcolor="bg",
|
@@ -19708,10 +19984,10 @@ class ComponentGenerator(Component):
|
|
19708
19984
|
|
19709
19985
|
Parameters
|
19710
19986
|
----------
|
19711
|
-
component_class : callable, usually a subclass of Component or Pdf or Cdf distribution
|
19987
|
+
component_class : callable, usually a subclass of Component or Pdf/Pmf or Cdf distribution
|
19712
19988
|
the type of components to be generated
|
19713
19989
|
|
19714
|
-
in case of a distribution, the Pdf or Cdf should return a callable
|
19990
|
+
in case of a distribution, the Pdf/Pmf or Cdf should return a callable
|
19715
19991
|
|
19716
19992
|
generator_name : str
|
19717
19993
|
name of the component generator.
|
@@ -19822,6 +20098,13 @@ class ComponentGenerator(Component):
|
|
19822
20098
|
|
19823
20099
|
e.g. env.main().activate()
|
19824
20100
|
|
20101
|
+
moments : iterable
|
20102
|
+
specifies the moments when the components have to be generated. It is not required that these are sorted.
|
20103
|
+
|
20104
|
+
note that the moments are specified in the current time unit
|
20105
|
+
|
20106
|
+
cannot be used together with at, delay, till, duration, number, iat,force_at, force_till, disturbance or equidistant
|
20107
|
+
|
19825
20108
|
env : Environment
|
19826
20109
|
environment where the component is defined
|
19827
20110
|
|
@@ -19850,6 +20133,7 @@ class ComponentGenerator(Component):
|
|
19850
20133
|
disturbance: Callable = None,
|
19851
20134
|
equidistant: bool = False,
|
19852
20135
|
at_end: Callable = None,
|
20136
|
+
moments: Iterable = None,
|
19853
20137
|
env: "Environment" = None,
|
19854
20138
|
**kwargs,
|
19855
20139
|
):
|
@@ -19867,6 +20151,15 @@ class ComponentGenerator(Component):
|
|
19867
20151
|
|
19868
20152
|
if not callable(component_class):
|
19869
20153
|
raise ValueError("component_class must be a callable")
|
20154
|
+
if moments is not None:
|
20155
|
+
if any(prop for prop in (at, delay, till, duration, number, iat, force_at, force_till, disturbance, equidistant)):
|
20156
|
+
raise ValueError(
|
20157
|
+
"specifying at, delay, till,duration, number, iat,force_at, force_till, disturbance or equidistant is not allowed, if moments is specified"
|
20158
|
+
)
|
20159
|
+
if callable(moments):
|
20160
|
+
moments = moments()
|
20161
|
+
moments = sorted([env.spec_to_time(moment) for moment in moments])
|
20162
|
+
|
19870
20163
|
self.component_class = component_class
|
19871
20164
|
self.iat = iat
|
19872
20165
|
self.disturbance = disturbance
|
@@ -19909,25 +20202,26 @@ class ComponentGenerator(Component):
|
|
19909
20202
|
at = None
|
19910
20203
|
process = ""
|
19911
20204
|
else:
|
19912
|
-
if self.iat is None and not equidistant:
|
19913
|
-
if
|
19914
|
-
|
19915
|
-
|
19916
|
-
|
19917
|
-
|
19918
|
-
|
19919
|
-
|
19920
|
-
if
|
19921
|
-
if
|
19922
|
-
|
19923
|
-
|
19924
|
-
|
19925
|
-
|
19926
|
-
|
19927
|
-
|
19928
|
-
|
19929
|
-
|
19930
|
-
|
20205
|
+
if (self.iat is None and not equidistant) or moments:
|
20206
|
+
if not moments:
|
20207
|
+
if till == inf or self.number == inf:
|
20208
|
+
raise ValueError("iat not specified --> till and number need to be specified")
|
20209
|
+
if disturbance is not None:
|
20210
|
+
raise ValueError("iat not specified --> disturbance not allowed")
|
20211
|
+
|
20212
|
+
moments = sorted([Uniform(at, till)() for _ in range(self.number)])
|
20213
|
+
if force_at or force_till:
|
20214
|
+
if number == 1:
|
20215
|
+
if force_at and force_till:
|
20216
|
+
raise ValueError("force_at and force_till does not allow number=1")
|
20217
|
+
moments = [at] if force_at else [till]
|
20218
|
+
else:
|
20219
|
+
v_at = at if force_at else moments[0]
|
20220
|
+
v_till = till if force_till else moments[-1]
|
20221
|
+
min_moment = moments[0]
|
20222
|
+
max_moment = moments[-1]
|
20223
|
+
moments = [interpolate(moment, min_moment, max_moment, v_at, v_till) for moment in moments]
|
20224
|
+
self.intervals = [t1 - t0 for t0, t1 in zip([0] + moments, moments)]
|
19931
20225
|
at = self.intervals[0]
|
19932
20226
|
self.intervals[0] = 0
|
19933
20227
|
process = "do_spread_yieldless" if env._yieldless else "do_spread"
|
@@ -20253,7 +20547,7 @@ class _Distribution:
|
|
20253
20547
|
If, after number_of_tries retries, the sampled value is still not within the given bounds,
|
20254
20548
|
fail_value will be returned
|
20255
20549
|
|
20256
|
-
Samples that cannot be converted (only possible with
|
20550
|
+
Samples that cannot be converted (only possible with /Pmf and CumPdf/CumPmf) to float
|
20257
20551
|
are assumed to be within the bounds.
|
20258
20552
|
"""
|
20259
20553
|
return Bounded(self, lowerbound, upperbound, fail_value, number_of_retries, include_lowerbound, include_upperbound).sample()
|
@@ -20493,7 +20787,7 @@ class Bounded(_Distribution):
|
|
20493
20787
|
If, after number_of_tries retries, the sampled value is still not within the given bounds,
|
20494
20788
|
fail_value will be returned
|
20495
20789
|
|
20496
|
-
Samples that cannot be converted to float (only possible with Pdf and CumPdf)
|
20790
|
+
Samples that cannot be converted to float (only possible with Pdf/Pmf and CumPdf)
|
20497
20791
|
are assumed to be within the bounds.
|
20498
20792
|
"""
|
20499
20793
|
|
@@ -21989,6 +22283,9 @@ class Pdf(_Distribution):
|
|
21989
22283
|
|
21990
22284
|
If it is a salabim distribution, not the distribution,
|
21991
22285
|
but a sample will be returned when calling sample.
|
22286
|
+
|
22287
|
+
|
22288
|
+
This method is also available under the name Pmf
|
21992
22289
|
"""
|
21993
22290
|
|
21994
22291
|
def __init__(self, spec: Union[Iterable, Dict], probabilities=None, time_unit: str = None, randomstream: Any = None, env: "Environment" = None):
|
@@ -22133,9 +22430,151 @@ class Pdf(_Distribution):
|
|
22133
22430
|
return self._mean
|
22134
22431
|
|
22135
22432
|
|
22433
|
+
class Pmf(Pdf):
|
22434
|
+
"""
|
22435
|
+
Probability mass function
|
22436
|
+
|
22437
|
+
Parameters
|
22438
|
+
----------
|
22439
|
+
spec : list, tuple or dict
|
22440
|
+
either
|
22441
|
+
|
22442
|
+
- if no probabilities specified:
|
22443
|
+
|
22444
|
+
list/tuple with x-values and corresponding probability
|
22445
|
+
dict where the keys are re x-values and the values are probabilities
|
22446
|
+
(x0, p0, x1, p1, ...xn,pn)
|
22447
|
+
|
22448
|
+
- if probabilities is specified:
|
22449
|
+
|
22450
|
+
list with x-values
|
22451
|
+
|
22452
|
+
probabilities : iterable or float
|
22453
|
+
if omitted, spec contains the probabilities
|
22454
|
+
|
22455
|
+
the iterable (p0, p1, ...pn) contains the probabilities of the corresponding
|
22456
|
+
x-values from spec.
|
22457
|
+
|
22458
|
+
alternatively, if a float is given (e.g. 1), all x-values
|
22459
|
+
have equal probability. The value is not important.
|
22460
|
+
|
22461
|
+
time_unit : str
|
22462
|
+
specifies the time unit
|
22463
|
+
|
22464
|
+
must be one of "years", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds"
|
22465
|
+
|
22466
|
+
default : no conversion
|
22467
|
+
|
22468
|
+
|
22469
|
+
randomstream : randomstream
|
22470
|
+
if omitted, random will be used
|
22471
|
+
|
22472
|
+
if used as random.Random(12299)
|
22473
|
+
it assigns a new stream with the specified seed
|
22474
|
+
|
22475
|
+
env : Environment
|
22476
|
+
environment where the distribution is defined
|
22477
|
+
|
22478
|
+
if omitted, default_env will be used
|
22479
|
+
|
22480
|
+
Note
|
22481
|
+
----
|
22482
|
+
p0+p1=...+pn>0
|
22483
|
+
|
22484
|
+
all densities are auto scaled according to the sum of p0 to pn,
|
22485
|
+
so no need to have p0 to pn add up to 1 or 100.
|
22486
|
+
|
22487
|
+
The x-values can be any type.
|
22488
|
+
|
22489
|
+
If it is a salabim distribution, not the distribution,
|
22490
|
+
but a sample will be returned when calling sample.
|
22491
|
+
|
22492
|
+
This method is also available under the name Pdf
|
22493
|
+
|
22494
|
+
"""
|
22495
|
+
|
22496
|
+
def __repr__(self):
|
22497
|
+
return "Pmf"
|
22498
|
+
|
22499
|
+
def print_info(self, as_str: bool = False, file: TextIO = None) -> str:
|
22500
|
+
"""
|
22501
|
+
prints information about the distribution
|
22502
|
+
|
22503
|
+
Parameters
|
22504
|
+
----------
|
22505
|
+
as_str: bool
|
22506
|
+
if False (default), print the info
|
22507
|
+
if True, return a string containing the info
|
22508
|
+
|
22509
|
+
file: file
|
22510
|
+
if None(default), all output is directed to stdout
|
22511
|
+
|
22512
|
+
otherwise, the output is directed to the file
|
22513
|
+
|
22514
|
+
Returns
|
22515
|
+
-------
|
22516
|
+
info (if as_str is True) : str
|
22517
|
+
"""
|
22518
|
+
result = []
|
22519
|
+
result.append("Pmf distribution " + hex(id(self)))
|
22520
|
+
result.append(" randomstream=" + hex(id(self.randomstream)))
|
22521
|
+
return return_or_print(result, as_str, file)
|
22522
|
+
|
22523
|
+
def sample(self, n: int = None) -> Any:
|
22524
|
+
"""
|
22525
|
+
Parameters
|
22526
|
+
----------
|
22527
|
+
n : number of samples : int
|
22528
|
+
if not specified, specifies just return one sample, as usual
|
22529
|
+
|
22530
|
+
if specified, return a list of n sampled values from the distribution without replacement.
|
22531
|
+
This requires that all probabilities are equal.
|
22532
|
+
|
22533
|
+
If n > number of values in the Pmf distribution, n is assumed to be the number of values
|
22534
|
+
in the distribution.
|
22535
|
+
|
22536
|
+
If a sampled value is a distribution, a sample from that distribution will be returned.
|
22537
|
+
|
22538
|
+
Returns
|
22539
|
+
-------
|
22540
|
+
Sample of the distribution : any (usually float) or list
|
22541
|
+
In case n is specified, returns a list of n values
|
22542
|
+
|
22543
|
+
"""
|
22544
|
+
if self.supports_n:
|
22545
|
+
if n is None:
|
22546
|
+
return self.randomstream.sample(self._x, 1)[0]
|
22547
|
+
else:
|
22548
|
+
if n < 0:
|
22549
|
+
raise ValueError("n < 0")
|
22550
|
+
n = min(n, len(self._x))
|
22551
|
+
xs = self.randomstream.sample(self._x, n)
|
22552
|
+
return [x.sample() if isinstance(x, _Distribution) else x for x in xs]
|
22553
|
+
else:
|
22554
|
+
if n is None:
|
22555
|
+
r = self.randomstream.random()
|
22556
|
+
for cum, x in zip([0] + self._cum, [0] + self._x):
|
22557
|
+
if r <= cum:
|
22558
|
+
if isinstance(x, _Distribution):
|
22559
|
+
return x.sample()
|
22560
|
+
return x
|
22561
|
+
else:
|
22562
|
+
raise ValueError("not all probabilities are the same")
|
22563
|
+
|
22564
|
+
def mean(self) -> float:
|
22565
|
+
"""
|
22566
|
+
Returns
|
22567
|
+
-------
|
22568
|
+
mean of the distribution : float
|
22569
|
+
if the mean can't be calculated (if not all x-values are scalars or distributions),
|
22570
|
+
nan will be returned.
|
22571
|
+
"""
|
22572
|
+
return self._mean
|
22573
|
+
|
22574
|
+
|
22136
22575
|
class CumPdf(_Distribution):
|
22137
22576
|
"""
|
22138
|
-
Cumulative Probability
|
22577
|
+
Cumulative Probability mass function
|
22139
22578
|
|
22140
22579
|
Parameters
|
22141
22580
|
----------
|
@@ -22188,6 +22627,8 @@ class CumPdf(_Distribution):
|
|
22188
22627
|
|
22189
22628
|
If it is a salabim distribution, not the distribution,
|
22190
22629
|
but a sample will be returned when calling sample.
|
22630
|
+
|
22631
|
+
This method is also available under the name CumPmf
|
22191
22632
|
"""
|
22192
22633
|
|
22193
22634
|
def __init__(
|
@@ -22303,6 +22744,116 @@ class CumPdf(_Distribution):
|
|
22303
22744
|
return self._mean
|
22304
22745
|
|
22305
22746
|
|
22747
|
+
class CumPmf(CumPdf):
|
22748
|
+
"""
|
22749
|
+
Cumulative Probability mass function
|
22750
|
+
|
22751
|
+
Parameters
|
22752
|
+
----------
|
22753
|
+
spec : list or tuple
|
22754
|
+
either
|
22755
|
+
|
22756
|
+
- if no cumprobabilities specified:
|
22757
|
+
|
22758
|
+
list with x-values and corresponding cumulative probability
|
22759
|
+
(x0, p0, x1, p1, ...xn,pn)
|
22760
|
+
|
22761
|
+
- if cumprobabilities is specified:
|
22762
|
+
|
22763
|
+
list with x-values
|
22764
|
+
|
22765
|
+
cumprobabilities : list, tuple or float
|
22766
|
+
if omitted, spec contains the probabilities
|
22767
|
+
|
22768
|
+
the list (p0, p1, ...pn) contains the cumulative probabilities of the corresponding
|
22769
|
+
x-values from spec.
|
22770
|
+
|
22771
|
+
|
22772
|
+
time_unit : str
|
22773
|
+
specifies the time unit
|
22774
|
+
|
22775
|
+
must be one of "years", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds"
|
22776
|
+
|
22777
|
+
default : no conversion
|
22778
|
+
|
22779
|
+
|
22780
|
+
randomstream : randomstream
|
22781
|
+
if omitted, random will be used
|
22782
|
+
|
22783
|
+
if used as random.Random(12299)
|
22784
|
+
it assigns a new stream with the specified seed
|
22785
|
+
|
22786
|
+
env : Environment
|
22787
|
+
environment where the distribution is defined
|
22788
|
+
|
22789
|
+
if omitted, default_env will be used
|
22790
|
+
|
22791
|
+
Note
|
22792
|
+
----
|
22793
|
+
p0<=p1<=..pn>0
|
22794
|
+
|
22795
|
+
all densities are auto scaled according to pn,
|
22796
|
+
so no need to have pn be 1 or 100.
|
22797
|
+
|
22798
|
+
The x-values can be any type.
|
22799
|
+
|
22800
|
+
If it is a salabim distribution, not the distribution,
|
22801
|
+
but a sample will be returned when calling sample.
|
22802
|
+
|
22803
|
+
This method is also available under the name CumPdf
|
22804
|
+
"""
|
22805
|
+
|
22806
|
+
def __repr__(self):
|
22807
|
+
return "CumPmf"
|
22808
|
+
|
22809
|
+
def print_info(self, as_str: bool = False, file: TextIO = None) -> str:
|
22810
|
+
"""
|
22811
|
+
prints information about the distribution
|
22812
|
+
|
22813
|
+
Parameters
|
22814
|
+
----------
|
22815
|
+
as_str: bool
|
22816
|
+
if False (default), print the info
|
22817
|
+
if True, return a string containing the info
|
22818
|
+
|
22819
|
+
file: file
|
22820
|
+
if None(default), all output is directed to stdout
|
22821
|
+
|
22822
|
+
otherwise, the output is directed to the file
|
22823
|
+
|
22824
|
+
Returns
|
22825
|
+
-------
|
22826
|
+
info (if as_str is True) : str
|
22827
|
+
"""
|
22828
|
+
result = []
|
22829
|
+
result.append("CumPmf distribution " + hex(id(self)))
|
22830
|
+
result.append(" randomstream=" + hex(id(self.randomstream)))
|
22831
|
+
return return_or_print(result, as_str, file)
|
22832
|
+
|
22833
|
+
def sample(self) -> Any:
|
22834
|
+
"""
|
22835
|
+
Returns
|
22836
|
+
-------
|
22837
|
+
Sample of the distribution : any (usually float)
|
22838
|
+
"""
|
22839
|
+
r = self.randomstream.random()
|
22840
|
+
for cum, x in zip([0] + self._cum, [0] + self._x):
|
22841
|
+
if r <= cum:
|
22842
|
+
if isinstance(x, _Distribution):
|
22843
|
+
return x.sample()
|
22844
|
+
return x
|
22845
|
+
|
22846
|
+
def mean(self) -> float:
|
22847
|
+
"""
|
22848
|
+
Returns
|
22849
|
+
-------
|
22850
|
+
mean of the distribution : float
|
22851
|
+
if the mean can't be calculated (if not all x-values are scalars or distributions),
|
22852
|
+
nan will be returned.
|
22853
|
+
"""
|
22854
|
+
return self._mean
|
22855
|
+
|
22856
|
+
|
22306
22857
|
class External(_Distribution):
|
22307
22858
|
"""
|
22308
22859
|
External distribution function
|
@@ -23902,7 +24453,7 @@ class _APNG:
|
|
23902
24453
|
|
23903
24454
|
def to_bytes(self):
|
23904
24455
|
CHUNK_BEFORE_IDAT = {"cHRM", "gAMA", "iCCP", "sBIT", "sRGB", "bKGD", "hIST", "tRNS", "pHYs", "sPLT", "tIME", "PLTE"}
|
23905
|
-
PNG_SIGN = b"\x89\x50\
|
24456
|
+
PNG_SIGN = b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
|
23906
24457
|
out = [PNG_SIGN]
|
23907
24458
|
other_chunks = []
|
23908
24459
|
seq = 0
|
@@ -26316,7 +26867,7 @@ def fonts():
|
|
26316
26867
|
|
26317
26868
|
for dir, recursive in dir_recursives:
|
26318
26869
|
for file_path in dir.glob("**/*.*" if recursive else "*.*"):
|
26319
|
-
if file_path.suffix.lower() == ".ttf":
|
26870
|
+
if file_path.suffix.lower() == ".ttf":
|
26320
26871
|
file = str(file_path)
|
26321
26872
|
fn = os.path.basename(file).split(".")[0]
|
26322
26873
|
if "_std_fonts" in globals() and fn in _std_fonts(): # test for availabiitly, because of minimized version
|
@@ -26376,7 +26927,8 @@ def getfont(fontname, fontsize):
|
|
26376
26927
|
return getfont.lookup[(fontname, fontsize)]
|
26377
26928
|
else:
|
26378
26929
|
getfont.lookup = {}
|
26379
|
-
|
26930
|
+
if fontname == "":
|
26931
|
+
a = 1
|
26380
26932
|
if isinstance(fontname, str):
|
26381
26933
|
fontlist1 = [fontname]
|
26382
26934
|
else:
|
@@ -26409,7 +26961,8 @@ def getfont(fontname, fontsize):
|
|
26409
26961
|
result = ImageFont.truetype(filename, size=int(fontsize))
|
26410
26962
|
else:
|
26411
26963
|
# refer to https://github.com/python-pillow/Pillow/issues/3730 for explanation (in order to load >= 500 fonts)
|
26412
|
-
|
26964
|
+
with open(filename, "rb") as f:
|
26965
|
+
result = ImageFont.truetype(font=io.BytesIO(f.read()), size=int(fontsize))
|
26413
26966
|
break
|
26414
26967
|
except Exception:
|
26415
26968
|
raise
|
@@ -26948,7 +27501,7 @@ def reset() -> None:
|
|
26948
27501
|
g.tkinter_loaded = "?"
|
26949
27502
|
g.image_container_cache = {}
|
26950
27503
|
g._default_cap_now = False
|
26951
|
-
g._captured_stdout=[]
|
27504
|
+
g._captured_stdout = []
|
26952
27505
|
|
26953
27506
|
random_seed() # always start with seed 1234567
|
26954
27507
|
|
@@ -27181,7 +27734,6 @@ reset()
|
|
27181
27734
|
set_environment_aliases()
|
27182
27735
|
|
27183
27736
|
if __name__ == "__main__":
|
27184
|
-
|
27185
27737
|
sys.path.insert(0, str(Path(__file__).parent / ".." / "misc"))
|
27186
27738
|
try:
|
27187
27739
|
import salabim_exp
|
@@ -1,5 +1,15 @@
|
|
1
|
-
import salabim as sim
|
2
1
|
import pytest
|
2
|
+
from pathlib import Path
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
|
6
|
+
if __name__ == "__main__":
|
7
|
+
file_folder = Path(__file__).parent
|
8
|
+
top_folder = (file_folder / "..").resolve()
|
9
|
+
sys.path.insert(0, str(top_folder))
|
10
|
+
os.chdir(file_folder)
|
11
|
+
|
12
|
+
import salabim as sim
|
3
13
|
|
4
14
|
|
5
15
|
class X(sim.Component):
|
@@ -190,9 +200,21 @@ def test_dis():
|
|
190
200
|
for component in components:
|
191
201
|
names.tally(component.name().split(".")[0])
|
192
202
|
|
203
|
+
def test_moments():
|
204
|
+
moments=(1,2,3,4,100,5)
|
205
|
+
components = exp(X, moments=moments)
|
206
|
+
assert components[0].enter_time(components) == 1
|
207
|
+
assert components[1].enter_time(components) == 2
|
208
|
+
assert components[2].enter_time(components) == 3
|
209
|
+
assert components[3].enter_time(components) == 4
|
210
|
+
assert components[4].enter_time(components) == 5
|
211
|
+
assert components[5].enter_time(components) == 100
|
212
|
+
|
213
|
+
|
193
214
|
|
194
215
|
# names.print_histogram(values=True, sort_on_weight=True)
|
195
216
|
|
196
217
|
if __name__ == "__main__":
|
197
|
-
pytest.main(["-vv", "-s",
|
218
|
+
pytest.main(["-vv", "-s",__file__])
|
219
|
+
|
198
220
|
|
@@ -1,7 +1,16 @@
|
|
1
|
-
import salabim as sim
|
2
1
|
import pytest
|
3
2
|
import collections
|
3
|
+
from pathlib import Path
|
4
|
+
import os
|
5
|
+
import sys
|
4
6
|
|
7
|
+
if __name__ == "__main__":
|
8
|
+
file_folder = Path(__file__).parent
|
9
|
+
top_folder = (file_folder / "..").resolve()
|
10
|
+
sys.path.insert(0, str(top_folder))
|
11
|
+
os.chdir(file_folder)
|
12
|
+
|
13
|
+
import salabim as sim
|
5
14
|
|
6
15
|
def collect(dis, n=10000):
|
7
16
|
m = sim.Monitor()
|
@@ -85,11 +94,13 @@ def test_int_uniform():
|
|
85
94
|
assert set(m.x()) == {1, 2, 3, 4, 5, 6}
|
86
95
|
count = collections.Counter(m.x())
|
87
96
|
for v, n in count.items():
|
88
|
-
assert n == pytest.approx(10000 / 6, rel=
|
97
|
+
assert n == pytest.approx(10000 / 6, rel=10)
|
89
98
|
|
90
99
|
|
91
100
|
def test_pdf():
|
92
101
|
env = sim.Environment()
|
102
|
+
d=sim.Pdf((1,10,2,70,3,20))
|
103
|
+
assert str(d)=="Pdf"
|
93
104
|
m = collect(sim.Pdf((1,10,2,70,3,20)))
|
94
105
|
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
95
106
|
m = collect(sim.Pdf((1,2,3),(10,70,20)))
|
@@ -100,6 +111,36 @@ def test_pdf():
|
|
100
111
|
m = collect(sim.Pdf(d))
|
101
112
|
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
102
113
|
|
114
|
+
|
115
|
+
def test_pmf():
|
116
|
+
env = sim.Environment()
|
117
|
+
d=sim.Pmf((1,10,2,70,3,20))
|
118
|
+
assert str(d)=="Pmf"
|
119
|
+
m = collect(sim.Pmf((1,10,2,70,3,20)))
|
120
|
+
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
121
|
+
m = collect(sim.Pmf((1,2,3),(10,70,20)))
|
122
|
+
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
123
|
+
d = {1:10, 2:70, 3:20}
|
124
|
+
m = collect(sim.Pmf(d.keys(),d.values()))
|
125
|
+
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
126
|
+
m = collect(sim.Pmf(d))
|
127
|
+
assert m.mean() == pytest.approx(2.1, rel=1e-2)
|
128
|
+
|
129
|
+
def test_cumpdf():
|
130
|
+
env = sim.Environment()
|
131
|
+
d=sim.CumPdf((1, 10, 2, 80, 3,100))
|
132
|
+
assert str(d)=="CumPdf"
|
133
|
+
m = collect(d)
|
134
|
+
assert m.mean() == pytest.approx(2, rel=1e-1)
|
135
|
+
|
136
|
+
def test_cumpmf():
|
137
|
+
env = sim.Environment()
|
138
|
+
d=sim.CumPmf((1, 10, 2, 80, 3,100))
|
139
|
+
assert str(d)=="CumPmf"
|
140
|
+
m = collect(d)
|
141
|
+
assert m.mean() == pytest.approx(2, rel=1e-1)
|
142
|
+
|
143
|
+
|
103
144
|
def test_scipy_distribution():
|
104
145
|
try:
|
105
146
|
import scipy.stats as st
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|