Coverage for moptipyapps / prodsched / rop_simulation.py: 92%
38 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 04:40 +0000
1"""
2A re-order-point-based simulation.
4Re-Order-Point (ROP) scenarios are such that for each product, a value `X` is
5provided. Once there are no more than `X` elements of that product in the
6warehouse, one new unit is ordered to be produced.
7Therefore, we have `n_products` such `X` values.
9ROP-based simulations extend the basic behavior of the class
10:class:`~moptipyapps.prodsched.simulation.Simulation` to re-order production
11based on ROPs.
13>>> from moptipyapps.prodsched.simulation import PrintingListener
14>>> instance = Instance(
15... name="test1", n_products=2, n_customers=4, n_stations=2, n_demands=4,
16... time_end_warmup=3000, time_end_measure=10000,
17... routes=[[0, 1], [1, 0]],
18... demands=[Demand(arrival=100, deadline=100, demand_id=0,
19... customer_id=0, product_id=0, amount=1, measure=False),
20... Demand(arrival=3100, deadline=3100, demand_id=1,
21... customer_id=1, product_id=0, amount=1, measure=True),
22... Demand(arrival=500, deadline=500, demand_id=2,
23... customer_id=2, product_id=1, amount=1, measure=False),
24... Demand(arrival=4000, deadline=5000, demand_id=3,
25... customer_id=3, product_id=1, amount=1, measure=True)],
26... warehous_at_t0=[0, 0],
27... station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
28... [ 5.0, 20.0, 7.0, 35.0, 4.0, 50.0]],
29... [[ 5.0, 24.0, 7.0, 80.0],
30... [ 3.0, 21.0, 6.0, 50.0,]]])
31>>> rop_sim = ROPSimulation(instance, PrintingListener(print_time=False))
32>>> rop_sim.set_rop((10, 20))
33>>> rop_sim.ctrl_run()
34start
35T=0.0: product=0, amount=0, in_warehouse=0, in_production=0, 0 pending demands
36T=0.0: station=0, 1 jobs queued
37T=0.0: start j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 0, sp: 0)\
38 at station 0
39T=0.0: product=1, amount=0, in_warehouse=0, in_production=0, 0 pending demands
40T=0.0: station=1, 1 jobs queued
41T=0.0: start j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 0, sp: 0)\
42 at station 1
43T=84.0: finished j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 0, sp: 0)\
44 at station 1
45T=100.0: product=0, amount=0, in_warehouse=0, in_production=11,\
46 1 pending demands
47T=130.0: finished j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 0,\
48 sp: 0) at station 0
49T=130.0: station=0, 2 jobs queued
50T=130.0: start j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 84,\
51 sp: 1) at station 0
52T=130.0: station=1, 1 jobs queued
53T=130.0: start j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 130,\
54 sp: 1) at station 1
55T=197.0: finished j(id: 0, p: 0, am: 11, ar: 0, me: F, c: T, st: 130,\
56 sp: 1) at station 1
57T=197.0: product=0, amount=11, in_warehouse=0, in_production=1,\
58 1 pending demands
59T=197.0: d(id: 0, p: 0, c: 0, am: 1, ar: 100, dl: 100, me: F) statisfied
60T=197.0: 10 units of product 0 in warehouse
61T=240.0: finished j(id: 1, p: 1, am: 21, ar: 0, me: F, c: T, st: 84,\
62 sp: 1) at station 0
63T=240.0: station=0, 1 jobs queued
64T=240.0: start j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 100,\
65 sp: 0) at station 0
66T=240.0: product=1, amount=21, in_warehouse=0, in_production=0,\
67 0 pending demands
68T=240.0: 21 units of product 1 in warehouse
69T=250.0: finished j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 100,\
70 sp: 0) at station 0
71T=250.0: station=1, 1 jobs queued
72T=250.0: start j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 250, sp: 1)\
73 at station 1
74T=255.0: finished j(id: 2, p: 0, am: 1, ar: 100, me: F, c: T, st: 250, sp: 1)\
75 at station 1
76T=255.0: product=0, amount=1, in_warehouse=10, in_production=0,\
77 0 pending demands
78T=255.0: 11 units of product 0 in warehouse
79T=500.0: product=1, amount=0, in_warehouse=21, in_production=0,\
80 1 pending demands
81T=500.0: d(id: 2, p: 1, c: 2, am: 1, ar: 500, dl: 500, me: F) statisfied
82T=500.0: 20 units of product 1 in warehouse
83T=500.0: station=1, 1 jobs queued
84T=500.0: start j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 500,\
85 sp: 0) at station 1
86T=503.0: finished j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 500,\
87 sp: 0) at station 1
88T=503.0: station=0, 1 jobs queued
89T=503.0: start j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 503,\
90 sp: 1) at station 0
91T=508.0: finished j(id: 3, p: 1, am: 1, ar: 500, me: F, c: T, st: 503,\
92 sp: 1) at station 0
93T=508.0: product=1, amount=1, in_warehouse=20, in_production=0,\
94 0 pending demands
95T=508.0: 21 units of product 1 in warehouse
96T=3100.0! product=0, amount=0, in_warehouse=11, in_production=0,\
97 1 pending demands
98T=3100.0! d(id: 1, p: 0, c: 1, am: 1, ar: 3100, dl: 3100, me: T) statisfied
99T=3100.0! 10 units of product 0 in warehouse
100T=3100.0! station=0, 1 jobs queued
101T=3100.0! start j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3100,\
102 sp: 0) at station 0
103T=3110.0! finished j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3100,\
104 sp: 0) at station 0
105T=3110.0! station=1, 1 jobs queued
106T=3110.0! start j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3110,\
107 sp: 1) at station 1
108T=3117.0! finished j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: T, st: 3110,\
109 sp: 1) at station 1
110T=3117.0! product=0, amount=1, in_warehouse=10, in_production=0,\
111 0 pending demands
112T=3117.0! 11 units of product 0 in warehouse
113T=4000.0! product=1, amount=0, in_warehouse=21, in_production=0,\
114 1 pending demands
115T=4000.0! d(id: 3, p: 1, c: 3, am: 1, ar: 4000, dl: 5000, me: T) statisfied
116T=4000.0! 20 units of product 1 in warehouse
117T=4000.0! station=1, 1 jobs queued
118T=4000.0! start j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4000,\
119 sp: 0) at station 1
120T=4003.0! finished j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4000,\
121 sp: 0) at station 1
122T=4003.0! station=0, 1 jobs queued
123T=4003.0! start j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4003,\
124 sp: 1) at station 0
125T=4008.0! finished j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: T, st: 4003,\
126 sp: 1) at station 0
127T=4008.0! product=1, amount=1, in_warehouse=20, in_production=0,\
128 0 pending demands
129T=4008.0! 21 units of product 1 in warehouse
130T=4008.0 -- finished
132>>> instance = Instance(
133... name="test2", n_products=2, n_customers=1, n_stations=2, n_demands=5,
134... time_end_warmup=21, time_end_measure=10000,
135... routes=[[0, 1], [1, 0]],
136... demands=[[0, 0, 1, 4, 20, 90], [1, 0, 0, 5, 22, 200],
137... [2, 0, 1, 4, 30, 200], [3, 0, 1, 6, 60, 200],
138... [4, 0, 0, 3, 5, 2000]],
139... warehous_at_t0=[2, 1],
140... station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
141... [ 5.0, 20.0, 7.0, 35.0, 4.0, 50.0]],
142... [[ 5.0, 24.0, 7.0, 80.0],
143... [ 3.0, 21.0, 6.0, 50.0,]]])
144>>> rop_sim = ROPSimulation(instance, PrintingListener(print_time=False))
145>>> rop_sim.set_rop((10, 10))
146>>> rop_sim.ctrl_run()
147start
148T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
149T=0.0: 2 units of product 0 in warehouse
150T=0.0: station=0, 1 jobs queued
151T=0.0: start j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 0, sp: 0)\
152 at station 0
153T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
154T=0.0: 1 units of product 1 in warehouse
155T=0.0: station=1, 1 jobs queued
156T=0.0: start j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 0, sp: 0)\
157 at station 1
158T=5.0: product=0, amount=0, in_warehouse=2, in_production=9, 1 pending demands
159T=20.0: product=1, amount=0, in_warehouse=1, in_production=10,\
160 1 pending demands
161T=22.0! product=0, amount=0, in_warehouse=2, in_production=12,\
162 2 pending demands
163T=30.0! product=1, amount=0, in_warehouse=1, in_production=14,\
164 2 pending demands
165T=39.0: finished j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 0, sp: 0)\
166 at station 1
167T=39.0! station=1, 2 jobs queued
168T=39.0: start j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 20, sp: 0)\
169 at station 1
170T=57.0: finished j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 20, sp: 0)\
171 at station 1
172T=57.0! station=1, 1 jobs queued
173T=57.0! start j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 30, sp: 0)\
174 at station 1
175T=60.0! product=1, amount=0, in_warehouse=1, in_production=18,\
176 3 pending demands
177T=69.0! finished j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 30, sp: 0)\
178 at station 1
179T=69.0! station=1, 1 jobs queued
180T=69.0! start j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 60, sp: 0)\
181 at station 1
182T=102.0! finished j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 60, sp: 0)\
183 at station 1
184T=110.0: finished j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 0, sp: 0)\
185 at station 0
186T=110.0! station=0, 6 jobs queued
187T=110.0: start j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 5, sp: 0)\
188 at station 0
189T=110.0! station=1, 1 jobs queued
190T=110.0: start j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 110, sp: 1)\
191 at station 1
192T=140.0: finished j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 5, sp: 0)\
193 at station 0
194T=140.0! station=0, 5 jobs queued
195T=140.0! start j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 22, sp: 0)\
196 at station 0
197T=171.0: finished j(id: 0, p: 0, am: 9, ar: 0, me: F, c: T, st: 110, sp: 1)\
198 at station 1
199T=171.0! station=1, 1 jobs queued
200T=171.0: start j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 140, sp: 1)\
201 at station 1
202T=171.0! product=0, amount=9, in_warehouse=2, in_production=8,\
203 2 pending demands
204T=171.0: d(id: 4, p: 0, c: 0, am: 3, ar: 5, dl: 2000, me: F) statisfied
205T=171.0! d(id: 1, p: 0, c: 0, am: 5, ar: 22, dl: 200, me: T) statisfied
206T=171.0! 3 units of product 0 in warehouse
207T=186.0: finished j(id: 2, p: 0, am: 3, ar: 5, me: F, c: T, st: 140, sp: 1)\
208 at station 1
209T=186.0! product=0, amount=3, in_warehouse=3, in_production=5,\
210 0 pending demands
211T=186.0! 6 units of product 0 in warehouse
212T=210.0! finished j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 22, sp: 0)\
213 at station 0
214T=210.0! station=0, 4 jobs queued
215T=210.0: start j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 39, sp: 1)\
216 at station 0
217T=210.0! station=1, 1 jobs queued
218T=210.0! start j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 210, sp: 1)\
219 at station 1
220T=245.0! finished j(id: 4, p: 0, am: 5, ar: 22, me: T, c: T, st: 210, sp: 1)\
221 at station 1
222T=245.0! product=0, amount=5, in_warehouse=6, in_production=0,\
223 0 pending demands
224T=245.0! 11 units of product 0 in warehouse
225T=263.0: finished j(id: 1, p: 1, am: 10, ar: 0, me: F, c: T, st: 39, sp: 1)\
226 at station 0
227T=263.0! station=0, 3 jobs queued
228T=263.0: start j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 57, sp: 1)\
229 at station 0
230T=263.0! product=1, amount=10, in_warehouse=1, in_production=14,\
231 3 pending demands
232T=263.0: d(id: 0, p: 1, c: 0, am: 4, ar: 20, dl: 90, me: F) statisfied
233T=263.0! d(id: 2, p: 1, c: 0, am: 4, ar: 30, dl: 200, me: T) statisfied
234T=263.0! 3 units of product 1 in warehouse
235T=287.0: finished j(id: 3, p: 1, am: 4, ar: 20, me: F, c: T, st: 57, sp: 1)\
236 at station 0
237T=287.0! station=0, 2 jobs queued
238T=287.0! start j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 69, sp: 1)\
239 at station 0
240T=287.0! product=1, amount=4, in_warehouse=3, in_production=10,\
241 1 pending demands
242T=287.0! d(id: 3, p: 1, c: 0, am: 6, ar: 60, dl: 200, me: T) statisfied
243T=287.0! 1 units of product 1 in warehouse
244T=303.0! finished j(id: 5, p: 1, am: 4, ar: 30, me: T, c: T, st: 69, sp: 1)\
245 at station 0
246T=303.0! station=0, 1 jobs queued
247T=303.0! start j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 102, sp: 1)\
248 at station 0
249T=303.0! product=1, amount=4, in_warehouse=1, in_production=6,\
250 0 pending demands
251T=303.0! 5 units of product 1 in warehouse
252T=337.0! finished j(id: 6, p: 1, am: 6, ar: 60, me: T, c: T, st: 102, sp: 1)\
253 at station 0
254T=337.0! product=1, amount=6, in_warehouse=5, in_production=0,\
255 0 pending demands
256T=337.0! 11 units of product 1 in warehouse
257T=337.0 -- finished
258"""
260from typing import Final, Iterable
262import numpy as np
263from pycommons.types import check_int_range, type_error
265from moptipyapps.prodsched.instance import (
266 Demand,
267 Instance,
268)
269from moptipyapps.prodsched.simulation import Listener, Simulation
272class ROPSimulation(Simulation):
273 """Create the re-order point-based simulation."""
275 def __init__(self, instance: Instance, listener: Listener) -> None:
276 """
277 Initialize this simulation.
279 :param instance: the instance
280 :param listener: the listener
281 """
282 super().__init__(instance, listener)
283 #: the re-order point
284 self.__rop: Final[list[int]] = [0] * instance.n_products
286 def set_rop(self, rop: Iterable[int] | np.ndarray) -> None:
287 """
288 Set the re-order point.
290 :param rop: the re-order point
291 """
292 if isinstance(rop, np.ndarray):
293 rop = map(int, rop) # type: ignore
294 if not isinstance(rop, Iterable):
295 raise type_error(rop, "rop", Iterable)
296 i: int = -1
297 for i, rv in enumerate(rop):
298 self.__rop[i] = check_int_range(
299 rv, "rop-value", 0, 1_000_000_000)
300 i += 1
301 n_prod: Final[int] = list.__len__(self.__rop)
302 if i != n_prod:
303 raise ValueError(f"Invalid rop length {i}, must be {n_prod}!")
305 def event_product(self, time: float, # pylint: disable=W0613,R0913,R0917
306 product_id: int, amount: int,
307 in_warehouse: int,
308 in_production: int, # pylint: disable=W0613
309 pending_demands: tuple[Demand, ...]) -> None:
310 """Perform a simulation step based on a product."""
311 # satisfy demands
312 available: int = in_warehouse + amount
313 unsatisfied: int = 0
314 for demand in pending_demands:
315 request: int = demand.amount
316 if request <= available:
317 self.act_demand_satisfied(demand)
318 available -= request
319 else:
320 unsatisfied += request
322 # update warehouse
323 if available < in_warehouse:
324 self.act_take_from_warehouse(product_id, in_warehouse - available)
325 elif available > in_warehouse:
326 self.act_store_in_warehouse(product_id, available - in_warehouse)
328 # check if re-order point needs to be triggered
329 rop: Final[int] = self.__rop[product_id]
330 available = available - unsatisfied + in_production
331 if available <= rop:
332 self.act_produce(product_id, rop - available + 1)