moptipyapps.prodsched package

Examples of production scheduling.

In this package, we provide instance data structures, instance generators, and a simulator for production scheduling problems. The goal is to emulate the works of Thürer et al. [1] and beyond, in a way that can be plugged into optimization procedures.

  1. Matthias Thürer, Nuno O. Fernandes, Hermann Lödding, and Mark Stevenson. Material Flow Control in Make-to-Stock Production Systems: An Assessment of Order Generation, Order Release and Production Authorization by Simulation Flexible Services and Manufacturing Journal. 37(1):1-37. March 2025. doi:https://doi.org/10.1007/s10696-024-09532-2

Subpackages

Submodules

moptipyapps.prodsched.instance module

A production scheduling instance.

Production instances have names Instance.name.

Notice that production times are used in a cycling fashion. The time when a certain product is finished can be computed via compute_finish_time() in an efficient way.

>>> name = "my_instance"

The number of products be 3.

>>> n_products = 3

The number of customers be 5.

>>> n_customers = 5

The number of stations be 4.

>>> n_stations = 4

There will be 6 customer demands.

>>> n_demands = 6

The end of the warmup period.

>>> time_end_warmup = 10

The end of the measurement period.

>>> time_end_measure = 10000

Each product may take a different route through different stations.

>>> route_p0 = [0, 3, 2]
>>> route_p1 = [0, 2, 1, 3]
>>> route_p2 = [1, 2, 3]
>>> routes = [route_p0, route_p1, route_p2]

Each demand is a tuple of demand_id, customer_id, product_id, amount, release time, and deadline.

>>> d0 = [0, 0, 1, 20, 1240,  3000]
>>> d1 = [1, 1, 0, 10, 2300,  4000]
>>> d2 = [2, 2, 2,  7, 8300, 11000]
>>> d3 = [3, 3, 1, 12, 7300,  9000]
>>> d4 = [4, 4, 2, 23, 5410, 16720]
>>> d5 = [5, 3, 0, 19, 4234, 27080]
>>> demands = [d0, d1, d2, d3, d4, d5]

There is a fixed amount of each product in the warehouse at time step 0.

>>> warehous_at_t0 = [10, 0, 6]

Each station requires a certain working time for each unit of each product. This production time may vary over time. For example, maybe station 0 needs 10 time units for 1 unit of product 0 from time step 0 to time step 19, then 11 time units from time step 20 to 39, then 8 time units from time step 40 to 59. These times are cyclic, meaning that at time step 60 to 79, it will again need 10 time units, and so on. Of course, production times are only specified for stations that a product is actually routed through.

>>> m0_p0 = [10.0, 20.0, 11.0, 40.0,  8.0, 60.0]
>>> m0_p1 = [12.0, 20.0,  7.0, 40.0, 11.0, 70.0]
>>> m0_p2 = []
>>> m1_p0 = []
>>> m1_p1 = [20.0, 50.0, 30.0, 120.0,  7.0, 200.0]
>>> m1_p2 = [21.0, 50.0, 29.0, 130.0,  8.0, 190.0]
>>> m2_p0 = [ 8.0, 20.0,  9.0, 60.0]
>>> m2_p1 = [10.0, 90.0]
>>> m2_p2 = [12.0, 70.0,  30.0, 120.0]
>>> m3_p0 = [70.0, 200.0,  3.0, 220.0]
>>> m3_p1 = [60.0, 220.0,  5.0, 260.0]
>>> m3_p2 = [30.0, 210.0, 10.0, 300.0]
>>> station_product_unit_times = [[m0_p0, m0_p1, m0_p2],
...                               [m1_p0, m1_p1, m1_p2],
...                               [m2_p0, m2_p1, m2_p2],
...                               [m3_p0, m3_p1, m3_p2]]

We can (but do not need to) provide additional information as key-value pairs.

>>> infos = {"source": "manually created",
...          "creation_date": "2025-11-09"}

From all of this data, we can create the instance.

>>> instance = Instance(name, n_products, n_customers, n_stations, n_demands,
...                     time_end_warmup, time_end_measure,
...                     routes, demands, warehous_at_t0,
...                     station_product_unit_times, infos)
>>> instance.name
'my_instance'
>>> instance.n_customers
5
>>> instance.n_stations
4
>>> instance.n_demands
6
>>> instance.n_products
3
>>> instance.routes
((0, 3, 2), (0, 2, 1, 3), (1, 2, 3))
>>> instance.time_end_warmup
10.0
>>> instance.time_end_measure
10000.0
>>> instance.demands
(Demand(arrival=1240.0, deadline=3000.0, demand_id=0, customer_id=0, product_id=1, amount=20, measure=True), Demand(arrival=2300.0, deadline=4000.0, demand_id=1, customer_id=1, product_id=0, amount=10, measure=True), Demand(arrival=4234.0, deadline=27080.0, demand_id=5, customer_id=3, product_id=0, amount=19, measure=True), Demand(arrival=5410.0, deadline=16720.0, demand_id=4, customer_id=4, product_id=2, amount=23, measure=True), Demand(arrival=7300.0, deadline=9000.0, demand_id=3, customer_id=3, product_id=1, amount=12, measure=True), Demand(arrival=8300.0, deadline=11000.0, demand_id=2, customer_id=2, product_id=2, amount=7, measure=True))
>>> instance.warehous_at_t0
(10, 0, 6)
>>> instance.station_product_unit_times
((array([10., 20., 11., 40.,  8., 60.]), array([12., 20.,  7., 40., 11., 70.]), array([], dtype=float64)), (array([], dtype=float64), array([ 20.,  50.,  30., 120.,   7., 200.]), array([ 21.,  50.,  29., 130.,   8., 190.])), (array([ 8., 20.,  9., 60.]), array([10., 90.]), array([ 12.,  70.,  30., 120.])), (array([ 70., 200.,   3., 220.]), array([ 60., 220.,   5., 260.]), array([ 30., 210.,  10., 300.])))
>>> instance.n_measurable_demands
6
>>> instance.n_measurable_demands_per_product
(2, 2, 2)
>>> dict(instance.infos)
{'source': 'manually created', 'creation_date': '2025-11-09'}

We can serialize instances to a stream of strings and also load them back from a stream of strings. Here, we store instance to a stream. We then load the independent instance i2 from that stream.

>>> i2 = from_stream(to_stream(instance))
>>> i2 is instance
False
>>> i2 == instance
True

You can see that the loaded instance has the same data as the stored one.

>>> i2.name == instance.name
True
>>> i2.n_customers == instance.n_customers
True
>>> i2.n_stations == instance.n_stations
True
>>> i2.n_demands == instance.n_demands
True
>>> i2.n_products == instance.n_products
True
>>> i2.routes == instance.routes
True
>>> i2.demands == instance.demands
True
>>> i2.time_end_warmup == instance.time_end_warmup
True
>>> i2.time_end_measure == instance.time_end_measure
True
>>> i2.warehous_at_t0 == instance.warehous_at_t0
True
>>> eq: bool = True
>>> for i in range(i2.n_stations):
...     ma1 = i2.station_product_unit_times[i]
...     ma2 = instance.station_product_unit_times[i]
...     for j in range(i2.n_products):
...         pr1 = ma1[j]
...         pr2 = ma2[j]
...         if not np.array_equal(pr1, pr2):
...             eq = False
>>> eq
True

True >>> i2.infos == instance.infos True

moptipyapps.prodsched.instance.DEMAND_AMOUNT: Final[int] = 3

the index of the demanded amount

moptipyapps.prodsched.instance.DEMAND_ARRIVAL: Final[int] = 4

the index of the demand release time

moptipyapps.prodsched.instance.DEMAND_CUSTOMER: Final[int] = 1

the index of the customer ID

moptipyapps.prodsched.instance.DEMAND_DEADLINE: Final[int] = 5

the index of the demand deadline

moptipyapps.prodsched.instance.DEMAND_ID: Final[int] = 0

the index of the demand ID

moptipyapps.prodsched.instance.DEMAND_PRODUCT: Final[int] = 2

the index of the product ID

class moptipyapps.prodsched.instance.Demand(arrival, deadline, demand_id, customer_id, product_id, amount, measure)[source]

Bases: Iterable[int | float]

The record for demands.

>>> Demand(arrival=0.6, deadline=0.8, demand_id=1,
...        customer_id=2, product_id=6, amount=12, measure=True)
Demand(arrival=0.6, deadline=0.8, demand_id=1, customer_id=2, product_id=6, amount=12, measure=True)
>>> Demand(arrival=16, deadline=28, demand_id=1,
...        customer_id=2, product_id=6, amount=12, measure=False)
Demand(arrival=16.0, deadline=28.0, demand_id=1, customer_id=2, product_id=6, amount=12, measure=False)
amount: int

the amount

arrival: float

the arrival time, i.e., when the demand enters the system

customer_id: int

the customer ID

deadline: float

the deadline, i.e., when the customer expects the result

demand_id: int

the ID of the demand

measure: bool

is this demand measurement relevant?

product_id: int

the ID of the product

class moptipyapps.prodsched.instance.Instance(name, n_products, n_customers, n_stations, n_demands, time_end_warmup, time_end_measure, routes, demands, warehous_at_t0, station_product_unit_times, infos=None)[source]

Bases: Component

An instance of the Production Scheduling Problem.

demands: Final[tuple[Demand, ...]]

The demands: Each demand is a tuple of demand_id, customer_id, product_id, amount, release_time, and deadline. The customer makes their order at time step release_time. They expect to receive their product by the deadline. The demands are sorted by release time and then deadline. The release time is always > 0. The deadline is always >= release time. Demand ids are unique.

infos: Final[Mapping[str, str]]

Additional information about the nature of the instance can be stored here. This has no impact on the behavior of the instance, but it may explain, e.g., settings of an instance generator.

n_customers: Final[int]

the number of customers in the scenario

n_demands: Final[int]

the number of demands in the scenario

n_measurable_demands: Final[int]

the number of demands that actually fall into the time measured window

n_measurable_demands_per_product: Final[tuple[int, ...]]

the measurable demands on a per-product basis

n_products: Final[int]

the number of products in the scenario

n_stations: Final[int]

the number of stations or workstations in the scenario

name: Final[str]

the name of this instance

routes: Final[tuple[tuple[int, ...], ...]]

the product routes, i.e., the stations through which each product must pass

station_product_unit_times: Final[tuple[tuple[ndarray, ...], ...]]

The per-station unit production times for each product. Each station can have different production times per product. Let’s say that this is tuple A. For each product, it has a tuple B at the index of the product id. If the product does not pass through the station, B is empty. Otherwise, it holds one or multiple tuples C. Each tuple C consists of two numbers: A per-unit-production time for the product. An end time index for this production time. Once the real time surpasses the end time of the last of these production specs, the production specs are recycled and begin again.

time_end_measure: Final[float]

the end of the measurement time

time_end_warmup: Final[float]

the end of the warmup time

warehous_at_t0: Final[tuple[int, ...]]

The units of product in the warehouse at time step 0. For each product, we have either 0 or a positive amount of product.

moptipyapps.prodsched.instance.KEY_DEMAND: Final[str] = 'demand'

the first part of the demand key

moptipyapps.prodsched.instance.KEY_IDX_END: Final[str] = ']'

the end of a key index

moptipyapps.prodsched.instance.KEY_IDX_START: Final[str] = '['

the start of a key index

moptipyapps.prodsched.instance.KEY_IN_WAREHOUSE: Final[str] = 'products_in_warehouse_at_t0'

The amount of products in the warehouse at time step 0.

moptipyapps.prodsched.instance.KEY_NAME: Final[str] = 'name'

the instance name key

moptipyapps.prodsched.instance.KEY_N_CUSTOMERS: Final[str] = 'n_customers'

the key for the number of customers

moptipyapps.prodsched.instance.KEY_N_DEMANDS: Final[str] = 'n_demands'

the number of demands in the scenario

moptipyapps.prodsched.instance.KEY_N_PRODUCTS: Final[str] = 'n_products'

the key for the number of products

moptipyapps.prodsched.instance.KEY_N_STATIONS: Final[str] = 'n_stations'

the key for the number of stations

moptipyapps.prodsched.instance.KEY_PRODUCTION_TIME: Final[str] = 'production_time'

the first part of the production time

moptipyapps.prodsched.instance.KEY_ROUTE: Final[str] = 'product_route'

the first part of the product route key

moptipyapps.prodsched.instance.KEY_TIME_END_MEASURE: Final[str] = 'time_end_measure'

the end of the measure period

moptipyapps.prodsched.instance.KEY_TIME_END_WARMUP: Final[str] = 'time_end_warmup'

the end of the warmup period

moptipyapps.prodsched.instance.MAX_ID: Final[int] = 1000000000

The maximum for the number of stations, products, or customers.

moptipyapps.prodsched.instance.MAX_VALUE: Final[int] = 2147483647

No value bigger than this is permitted in any tuple anywhere.

moptipyapps.prodsched.instance.compute_finish_time(start_time, amount, production_times)[source]

Compute the time when one job is finished.

The production times are cyclic intervals of unit production times and interval ends.

Parameters:
  • start_time (float) – the starting time of the job

  • amount (int) – the number of units to be produced

  • production_times (ndarray) – the production times array

Return type:

float

Returns:

the end time

Here, the production time is 10 time units / 1 product unit, valid until end time 100.

>>> compute_finish_time(0.0, 1, np.array((10.0, 100.0), np.float64))
10.0

Here, the production time is 10 time units / 1 product unit, valid until end time 100. We begin producing at time unit 250. Since the production periods are cyclic, this is OK: we would be halfway through the third production period when the request comes in. It will consume 10 time units and be done at time unit 260.

>>> compute_finish_time(250.0, 1, np.array((10.0, 100.0)))
260.0

Here, the end time of the production time validity is at time unit 100. However, we begin producing 1 product unit at time step 90. This unit will use 10 time units, meaning that its production is exactly finished when the production time validity ends. It will be finished at time step 100.

>>> compute_finish_time(90.0, 1, np.array((10.0, 100.0)))
100.0

Here, the end time of the production time validity is at time unit 100. However, we begin producing 1 product unit at time step 95. This unit would use 10 time units. It will use these units, even though this extends beyond the end of the production time window.

>>> compute_finish_time(95.0, 1, np.array((10.0, 100.0)))
105.0

Now we have two production periods. The production begins again at time step 95. It will use 10 time units, even though this extends into the second period.

>>> compute_finish_time(95.0, 1, np.array((10.0, 100.0, 20.0, 200.0)))
105.0

Now things get more complex. We want to do 10 units of product. We start in the first period, so one unit will be completed there. This takes the starting time for the next job to 105, which is in the second period. Here, one unit of product takes 20 time units. We can finish producing one unit until time 125 and start the production of a second one, taking until 145. Now the remaining three units are produced until time 495 >>> compute_finish_time(95.0, 10, np.array(( … 10.0, 100.0, 20.0, 140.0, 50.0, 5000.0))) 495.0 >>> 95 + (1*10 + 2*20 + 7*50) 495

We again produce 10 product units starting at time step 95. The first one takes 10 time units, taking us into the second production interval at time 105. Then we can again do two units here, which consume 40 time units, taking us over the edge into the third interval at time unit 145. Here we do two units using 50 time units. We ahen are at time 245, which wraps back to 45. So the remaining 5 units take 10 time units each.

>>> compute_finish_time(95.0, 10, np.array((
...     10.0, 100.0, 20.0, 140.0, 50.0, 200.0)))
295.0
>>> 95 + (1*10 + 2*20 + 2*50 + 5*10)
295

This is the same as the last example, but this time, the last interval (3 time units until 207) is skipped over by the long production of the second 50-time-unit product.

>>> compute_finish_time(95.0, 10, np.array((
...     10.0, 100.0, 20.0, 140.0, 50.0, 200.0,  3.0, 207.0)))
295.0
>>> 95 + (1*10 + 2*20 + 2*50 + 5*10)
295

Production unit times may extend beyond the intervals.

>>> compute_finish_time(0.0, 5, np.array((1000.0, 100.0, 10.0, 110.0)))
5000.0
>>>
5 * 1000
moptipyapps.prodsched.instance.from_stream(stream)[source]

Read an instance from a data stream.

Parameters:

stream (Iterable[str]) – the data stream

Return type:

Instance

Returns:

the instance

moptipyapps.prodsched.instance.instance_sort_key(inst)[source]

Get a sort key for instances.

Parameters:

inst (Instance) – the instance

Return type:

str

Returns:

the sort key

moptipyapps.prodsched.instance.load_instances(source)[source]

Load the instances from a given irectory.

Parameters:

source (str) – the source directory

Return type:

tuple[Instance, ...]

Returns:

the tuple of instances

>>> inst1 = Instance(
...     name="test1", n_products=1, n_customers=1, n_stations=2,
...     n_demands=1, time_end_warmup=10, time_end_measure=4000,
...     routes=[[0, 1]],
...     demands=[[0, 0, 0, 10, 20, 100]],
...     warehous_at_t0=[0],
...     station_product_unit_times=[[[10.0, 10000.0]],
...                                 [[30.0, 10000.0]]])
>>> inst2 = Instance(
...     name="test2", n_products=2, n_customers=1, n_stations=2,
...      n_demands=3, time_end_warmup=21, time_end_measure=10000,
...     routes=[[0, 1], [1, 0]],
...     demands=[[0, 0, 1, 10, 20, 90], [1, 0, 0, 5, 22, 200],
...              [2, 0, 1, 7, 30, 200]],
...     warehous_at_t0=[2, 1],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> from pycommons.io.temp import temp_dir
>>> with temp_dir() as td:
...     store_instances(td, [inst2, inst1])
...     res = load_instances(td)
>>> res == (inst1, inst2)
True
moptipyapps.prodsched.instance.store_instances(dest, instances)[source]

Store an iterable of instances to the given directory.

Parameters:
Return type:

None

moptipyapps.prodsched.instance.to_stream(instance)[source]

Convert an instance to a stream of data.

Parameters:

instance (Instance) – the instance to convert to a stream

Return type:

Generator[str, None, None]

Returns:

the stream of data

moptipyapps.prodsched.instances module

Generate instances for training and testing.

In this package, we provide a function for generating instances in a deterministic fashion for training and testing of MFC scenarios.

The function get_instances() will return a fixed set of instances for a given instance number. It allows you to store and retrieve compatible instance sets of different sizes from a given directory. It is not very efficient, but it will do.

>>> from pycommons.io.temp import temp_dir
>>> with temp_dir() as td:
...     inst_1 = get_instances(3, td)
...     inst_2 = get_instances(1, td)
...     inst_3 = get_instances(5, td)
>>> len(inst_1)
3
>>> len(inst_2)
1
>>> len(inst_3)
5
>>> all(ix in inst_1 for ix in inst_2)
True
>>> all(ix in inst_3 for ix in inst_1)
True
moptipyapps.prodsched.instances.get_instances(n, inst_dir)[source]

Get the instances for the experiment.

Parameters:
  • n (int) – the expected number of instances

  • inst_dir (str) – the instance directory

Return type:

tuple[Instance, ...]

moptipyapps.prodsched.mfc_generator module

Methods for generating MFC instances.

Here we provide some basic utilities for generating deterministic variants of instances such as those used in the paper [1] by Thürer et al.

>>> from moptipyapps.utils.sampling import Gamma
>>> inst = sample_mfc_instance([
...  Product(0, (0, 1), Gamma.from_alpha_beta(3, 0.26))], [
...  Station(0, Gamma.from_k_and_mean(3, 10)),
...  Station(1, Gamma.from_k_and_mean(2, 10))],
...  time_end_measure=100, seed=123)
>>> inst.name
'mfc_1_2_100_0x7b'
>>> inst.n_demands
7
>>> inst.demands
(Demand(arrival=5.213885878801001, deadline=5.213885878801001, demand_id=0, customer_id=0, product_id=0, amount=1, measure=False), Demand(arrival=25.872387132411287, deadline=25.872387132411287, demand_id=1, customer_id=1, product_id=0, amount=1, measure=False), Demand(arrival=43.062182155666896, deadline=43.062182155666896, demand_id=2, customer_id=2, product_id=0, amount=1, measure=True), Demand(arrival=49.817978344678004, deadline=49.817978344678004, demand_id=3, customer_id=3, product_id=0, amount=1, measure=True), Demand(arrival=58.21166922638016, deadline=58.21166922638016, demand_id=4, customer_id=4, product_id=0, amount=1, measure=True), Demand(arrival=69.09054693162531, deadline=69.09054693162531, demand_id=5, customer_id=5, product_id=0, amount=1, measure=True), Demand(arrival=88.804579148131, deadline=88.804579148131, demand_id=6, customer_id=6, product_id=0, amount=1, measure=True))
>>> len(inst.station_product_unit_times[0][0])
1600
>>> len(inst.station_product_unit_times[1][0])
1600
>>> inst.time_end_measure
100.0
>>> inst.time_end_warmup
30.0
>>> d = dict(inst.infos)
>>> del d["info_generated_on"]
>>> del d["info_generator_version"]
>>> d
{'info_generator': 'moptipyapps.prodsched.mfc_generator', 'info_rand_seed_src': 'USER_PROVIDED', 'info_rand_seed': '0x7b', 'info_time_end_measure_src': 'USER_PROVIDED', 'info_time_end_measure': '100', 'info_time_end_warmup_src': 'SAMPLED', 'info_time_end_warmup': '30', 'info_name_src': 'SAMPLED', 'info_product_interarrival_times[0]': 'Erlang(k=3, theta=3.846153846153846)', 'info_product_route[0]': 'USER_PROVIDED', 'info_station_processing_time[0]': 'Erlang(k=3, theta=3.3333333333333335)', 'info_station_processing_time_window_length[0]': 'Const(v=0.125)', 'info_station_processing_time[1]': 'Erlang(k=2, theta=5)', 'info_station_processing_time_window_length[1]': 'Const(v=0.125)'}
>>> inst = sample_mfc_instance(seed=23445)
>>> inst.name
'mfc_10_13_10000_0x5b95'
>>> inst.n_demands
9922
>>> len([dem for dem in inst.demands if dem.product_id == 0])
959
>>> len([dem for dem in inst.demands if dem.product_id == 1])
1055
>>> [len(k[0]) for k in inst.station_product_unit_times]
[160000, 160000, 0, 160000, 0, 0, 0, 0, 160000, 160000, 160000, 0, 0]
  1. Matthias Thürer, Nuno O. Fernandes, Hermann Lödding, and Mark Stevenson. Material Flow Control in Make-to-Stock Production Systems: An Assessment of Order Generation, Order Release and Production Authorization by Simulation Flexible Services and Manufacturing Journal. 37(1):1-37. March 2025. doi:https://doi.org/10.1007/s10696-024-09532-2

moptipyapps.prodsched.mfc_generator.INFO_GENERATED_ON: Final[str] = 'info_generated_on'

When was the instance generated?

moptipyapps.prodsched.mfc_generator.INFO_GENERATOR: Final[str] = 'info_generator'

The generator key

moptipyapps.prodsched.mfc_generator.INFO_GENERATOR_VERSION: Final[str] = 'info_generator_version'

The generator version

moptipyapps.prodsched.mfc_generator.INFO_NAME_SRC: Final[str] = 'info_name_src'

the name source

moptipyapps.prodsched.mfc_generator.INFO_PRODUCT_INTERARRIVAL_TIME_DIST: Final[str] = 'info_product_interarrival_times'

The information key for interarrival times

moptipyapps.prodsched.mfc_generator.INFO_PRODUCT_ROUTE: Final[str] = 'info_product_route'

The information key for interarrival times

moptipyapps.prodsched.mfc_generator.INFO_RAND_SEED: Final[str] = 'info_rand_seed'

the random seed

moptipyapps.prodsched.mfc_generator.INFO_RAND_SEED_SRC: Final[str] = 'info_rand_seed_src'

the random seed source

moptipyapps.prodsched.mfc_generator.INFO_SAMPLED: Final[str] = 'SAMPLED'

a sampled structure

moptipyapps.prodsched.mfc_generator.INFO_STATION_PROCESSING_TIME: Final[str] = 'info_station_processing_time'

The information key for the processing time distribution

moptipyapps.prodsched.mfc_generator.INFO_STATION_PROCESSING_WINDOW_LENGTH: Final[str] = 'info_station_processing_time_window_length'

The information key for the processing time window length distribution

moptipyapps.prodsched.mfc_generator.INFO_TIME_END_MEASURE: Final[str] = 'info_time_end_measure'

the measurement time end

moptipyapps.prodsched.mfc_generator.INFO_TIME_END_MEASURE_SRC: Final[str] = 'info_time_end_measure_src'

the source of the measurement time end

moptipyapps.prodsched.mfc_generator.INFO_TIME_END_WARMUP: Final[str] = 'info_time_end_warmup'

the warmup time end

moptipyapps.prodsched.mfc_generator.INFO_TIME_END_WARMUP_SRC: Final[str] = 'info_time_end_warmup_src'

the source of the warmup time end

moptipyapps.prodsched.mfc_generator.INFO_USER_PROVIDED: Final[str] = 'USER_PROVIDED'

a fixed structure

class moptipyapps.prodsched.mfc_generator.Product(product_id, routing, interarrival_times)[source]

Bases: object

The product sampling definition.

interarrival_times: Distribution

the interarrival distribution

log_info(infos)[source]

Log the sampling information of this product to the infos dict.

Parameters:

infos (dict[str, str]) – the information dictionary

Return type:

None

product_id: int

the product ID

routing: tuple[int, ...]

the routing of the product

class moptipyapps.prodsched.mfc_generator.Station(station_id, processing_time, processing_windows=None)[source]

Bases: object

The station sampling definition.

log_info(infos)[source]

Log the sampling information of this product to the infos dict.

Parameters:

infos (dict[str, str]) – the information dictionary

Return type:

None

processing_time: Distribution

the processing time distribution

processing_windows: Distribution

the processing window distribution

station_id: int

the product ID

moptipyapps.prodsched.mfc_generator.default_products()[source]

Create the default product sequence as used in [1].

Return type:

tuple[Product, ...]

Returns:

the default product sequence

>>> default_products()
(Product(product_id=0, routing=(0, 1, 3, 1, 8, 9, 10), interarrival_times=Erlang(k=3, theta=3.3333333333333335)), Product(product_id=1, routing=(0, 1, 4, 1, 7, 8, 9, 10), interarrival_times=Erlang(k=2, theta=5)), Product(product_id=2, routing=(0, 1, 5, 3, 1, 8, 11, 10), interarrival_times=Uniform(low=5, high=15)), Product(product_id=3, routing=(0, 1, 6, 3, 1, 8, 9, 10), interarrival_times=Erlang(k=3, theta=3.3333333333333335)), Product(product_id=4, routing=(0, 1, 3, 11, 1, 8, 1, 12), interarrival_times=Erlang(k=4, theta=2.5)), Product(product_id=5, routing=(0, 1, 4, 11, 1, 8, 6, 12), interarrival_times=Erlang(k=2, theta=5)), Product(product_id=6, routing=(0, 1, 5, 11, 1, 7, 1, 12), interarrival_times=Erlang(k=4, theta=2.5)), Product(product_id=7, routing=(0, 1, 2, 6, 3, 11, 1, 7, 5, 8, 1, 12), interarrival_times=Uniform(low=5, high=15)), Product(product_id=8, routing=(0, 1, 2, 4, 3, 5, 11, 1, 7, 1, 9, 5, 12), interarrival_times=Erlang(k=4, theta=2.5)), Product(product_id=9, routing=(0, 1, 2, 5, 1, 3, 11, 6, 1, 8, 10, 4, 12), interarrival_times=Erlang(k=2, theta=5)))
  1. Matthias Thürer, Nuno O. Fernandes, Hermann Lödding, and Mark Stevenson. Material Flow Control in Make-to-Stock Production Systems: An Assessment of Order Generation, Order Release and Production Authorization by Simulation Flexible Services and Manufacturing Journal. 37(1):1-37. March 2025. doi:https://doi.org/10.1007/s10696-024-09532-2

moptipyapps.prodsched.mfc_generator.default_stations()[source]

Create the default station sequence as used in [1].

Return type:

tuple[Station, ...]

Returns:

the default product station

>>> default_stations()
(Station(station_id=0, processing_time=Erlang(k=3, theta=0.26), processing_windows=Const(v=0.125)), Station(station_id=1, processing_time=Erlang(k=3, theta=0.12), processing_windows=Const(v=0.125)), Station(station_id=2, processing_time=Erlang(k=2, theta=1.33), processing_windows=Const(v=0.125)), Station(station_id=3, processing_time=AtLeast(lb=5e-324, d=Exponential(eta=1)), processing_windows=Const(v=0.125)), Station(station_id=4, processing_time=Erlang(k=3, theta=0.67), processing_windows=Const(v=0.125)), Station(station_id=5, processing_time=Erlang(k=4, theta=0.35), processing_windows=Const(v=0.125)), Station(station_id=6, processing_time=Erlang(k=3, theta=0.59), processing_windows=Const(v=0.125)), Station(station_id=7, processing_time=Erlang(k=3, theta=0.63), processing_windows=Const(v=0.125)), Station(station_id=8, processing_time=Erlang(k=2, theta=0.59), processing_windows=Const(v=0.125)), Station(station_id=9, processing_time=Erlang(k=3, theta=0.6), processing_windows=Const(v=0.125)), Station(station_id=10, processing_time=AtLeast(lb=5e-324, d=Exponential(eta=1)), processing_windows=Const(v=0.125)), Station(station_id=11, processing_time=Erlang(k=4, theta=0.29), processing_windows=Const(v=0.125)), Station(station_id=12, processing_time=Erlang(k=3, theta=0.48), processing_windows=Const(v=0.125)))
  1. Matthias Thürer, Nuno O. Fernandes, Hermann Lödding, and Mark Stevenson. Material Flow Control in Make-to-Stock Production Systems: An Assessment of Order Generation, Order Release and Production Authorization by Simulation Flexible Services and Manufacturing Journal. 37(1):1-37. March 2025. doi:https://doi.org/10.1007/s10696-024-09532-2

moptipyapps.prodsched.mfc_generator.sample_mfc_instance(products=None, stations=None, time_end_warmup=None, time_end_measure=None, name=None, seed=None)[source]

Sample an MFC instance.

Parameters:
  • products (Optional[Iterable[Product]], default: None) – the products

  • stations (Optional[Iterable[Station]], default: None) – the work stations

  • time_end_warmup (int | float | None, default: None) – the end of the warmup period

  • time_end_measure (int | float | None, default: None) – the end of the measurement period

  • name (str | None, default: None) – the instance name

  • seed (int | None, default: None) – the random seed, if any

Return type:

Instance

Returns:

the instance

moptipyapps.prodsched.multistatistics module

A statistics record for multiple simulations.

We use this record as the solution space when optimizing for the MFC scenario. It stores the statistics of several simulation runs. The objective functions can then access these statistics. A space instance is provided that can create, copy, and serialize these objects to text, so that they can appear in the log files.

class moptipyapps.prodsched.multistatistics.MultiStatistics(instances)[source]

Bases: object

A set of statistics gathered over multiple instances.

inst_names: tuple[str, ...]

the instance names

per_instance: tuple[Statistics, ...]

the per-instance statistics

class moptipyapps.prodsched.multistatistics.MultiStatisticsSpace(instances)[source]

Bases: Space

An implementation of the Space API of for multiple statistics.

copy(dest, source)[source]

Copy one multi-statistics to another one.

Parameters:
Return type:

None

create()[source]

Create an empty multi-statistics record.

Return type:

MultiStatistics

Returns:

the empty multi-statistics record

from_str(text)[source]

Convert a string to a multi-statistics.

Parameters:

text (str) – the string

Return type:

MultiStatistics

Returns:

the multi-statistics

instances: Final[tuple[Instance, ...]]

The instance to which the packings apply.

is_equal(x1, x2)[source]

Check if two multi-statistics have the same contents.

Parameters:
Return type:

bool

Returns:

True if both multi-statistics have the same content

log_parameters_to(logger)[source]

Log the parameters of the space to the given logger.

Parameters:

logger (KeyValueLogSection) – the logger for the parameters

Return type:

None

n_points()[source]

Get the number of possible multi-statistics.

Return type:

int

Returns:

just some arbitrary number

to_str(x)[source]

Convert a multi-statistics to a string.

Parameters:

x (MultiStatistics) – the packing

Return type:

str

Returns:

a string corresponding to the multi-statistics

validate(x)[source]

Check if a multi-statistics is valid.

Parameters:

x (MultiStatistics) – the multi-statistics

Raises:
  • TypeError – if any component of the multi-statistics is of the wrong type

  • ValueError – if the multi-statistics is not feasible

Return type:

None

moptipyapps.prodsched.multistatistics.to_stream(multi)[source]

Convert a multi-statistics object to a stream.

Parameters:

multi (MultiStatistics) – the multi-statistics object

Return type:

Generator[str, None, None]

Returns:

the stream of strings

moptipyapps.prodsched.rop_experiment module

A small template for ROP-based experiments.

This experiment uses the NSGA-II algorithm to optimize Re-Order-Points (ROPs) to achieve both a high worst-case fillrate and a low worst-case average stock level.

moptipyapps.prodsched.rop_experiment.run(dest, instances, n_inst, n_runs, max_fes, ps)[source]

Run the experiment.

Parameters:
  • dest (str) – the destination directory

  • instances (str) – the directory with the instances

  • n_inst (int) – the number of instances

  • n_runs (int) – the number of runs

  • max_fes (int) – the maximum FEs

  • ps (int) – the population size

Return type:

None

moptipyapps.prodsched.rop_multisimulation module

A simulator for multiple runs of the ROP scenario.

Re-Order-Point (ROP) scenarios are such that for each product, a value X is provided. Once there are no more than X elements of that product in the warehouse, one new unit is ordered to be produced. Therefore, we have n_products such X values.

This module provides the functionality to simulate this scenario over multiple instances.

class moptipyapps.prodsched.rop_multisimulation.ROPMultiSimulation(instances)[source]

Bases: Encoding

A multi-simulation.

decode(x, y)[source]

Map a ROP setting to a multi-statistics.

Parameters:
Return type:

None

moptipyapps.prodsched.rop_simulation module

A re-order-point-based simulation.

Re-Order-Point (ROP) scenarios are such that for each product, a value X is provided. Once there are no more than X elements of that product in the warehouse, one new unit is ordered to be produced. Therefore, we have n_products such X values.

ROP-based simulations extend the basic behavior of the class Simulation to re-order production based on ROPs.

>>> from moptipyapps.prodsched.simulation import PrintingListener
>>> instance = Instance(
...     name="test1", n_products=2, n_customers=4, n_stations=2, n_demands=4,
...     time_end_warmup=3000, time_end_measure=10000,
...     routes=[[0, 1], [1, 0]],
...     demands=[Demand(arrival=100, deadline=100, demand_id=0,
...                     customer_id=0, product_id=0, amount=1, measure=False),
...              Demand(arrival=3100, deadline=3100, demand_id=1,
...                     customer_id=1, product_id=0, amount=1, measure=True),
...              Demand(arrival=500, deadline=500, demand_id=2,
...                     customer_id=2, product_id=1, amount=1, measure=False),
...              Demand(arrival=4000, deadline=5000, demand_id=3,
...                     customer_id=3, product_id=1, amount=1, measure=True)],
...     warehous_at_t0=[0, 0],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> rop_sim = ROPSimulation(instance, PrintingListener(print_time=False))
>>> rop_sim.set_rop((10, 20))
>>> rop_sim.ctrl_run()
start
T=0.0: product=0, amount=0, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: station=0, 1 jobs queued
T=0.0: start j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 0, sp: 0) at station 0
T=0.0: product=1, amount=0, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: station=1, 1 jobs queued
T=0.0: start j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 0, sp: 0) at station 1
T=84.0: finished j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 0, sp: 0) at station 1
T=100.0: product=0, amount=0, in_warehouse=0, in_production=11, 1 pending demands
T=130.0: finished j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 0, sp: 0) at station 0
T=130.0: station=0, 2 jobs queued
T=130.0: start j(id: 1, p: 1, am: 21, ar: 0, me: F, c: F, st: 84, sp: 1) at station 0
T=130.0: station=1, 1 jobs queued
T=130.0: start j(id: 0, p: 0, am: 11, ar: 0, me: F, c: F, st: 130, sp: 1) at station 1
T=197.0: finished j(id: 0, p: 0, am: 11, ar: 0, me: F, c: T, st: 130, sp: 1) at station 1
T=197.0: product=0, amount=11, in_warehouse=0, in_production=1, 1 pending demands
T=197.0: d(id: 0, p: 0, c: 0, am: 1, ar: 100, dl: 100, me: F) statisfied
T=197.0: 10 units of product 0 in warehouse
T=240.0: finished j(id: 1, p: 1, am: 21, ar: 0, me: F, c: T, st: 84, sp: 1) at station 0
T=240.0: station=0, 1 jobs queued
T=240.0: start j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 100, sp: 0) at station 0
T=240.0: product=1, amount=21, in_warehouse=0, in_production=0, 0 pending demands
T=240.0: 21 units of product 1 in warehouse
T=250.0: finished j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 100, sp: 0) at station 0
T=250.0: station=1, 1 jobs queued
T=250.0: start j(id: 2, p: 0, am: 1, ar: 100, me: F, c: F, st: 250, sp: 1) at station 1
T=255.0: finished j(id: 2, p: 0, am: 1, ar: 100, me: F, c: T, st: 250, sp: 1) at station 1
T=255.0: product=0, amount=1, in_warehouse=10, in_production=0, 0 pending demands
T=255.0: 11 units of product 0 in warehouse
T=500.0: product=1, amount=0, in_warehouse=21, in_production=0, 1 pending demands
T=500.0: d(id: 2, p: 1, c: 2, am: 1, ar: 500, dl: 500, me: F) statisfied
T=500.0: 20 units of product 1 in warehouse
T=500.0: station=1, 1 jobs queued
T=500.0: start j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 500, sp: 0) at station 1
T=503.0: finished j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 500, sp: 0) at station 1
T=503.0: station=0, 1 jobs queued
T=503.0: start j(id: 3, p: 1, am: 1, ar: 500, me: F, c: F, st: 503, sp: 1) at station 0
T=508.0: finished j(id: 3, p: 1, am: 1, ar: 500, me: F, c: T, st: 503, sp: 1) at station 0
T=508.0: product=1, amount=1, in_warehouse=20, in_production=0, 0 pending demands
T=508.0: 21 units of product 1 in warehouse
T=3100.0! product=0, amount=0, in_warehouse=11, in_production=0, 1 pending demands
T=3100.0! d(id: 1, p: 0, c: 1, am: 1, ar: 3100, dl: 3100, me: T) statisfied
T=3100.0! 10 units of product 0 in warehouse
T=3100.0! station=0, 1 jobs queued
T=3100.0! start j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3100, sp: 0) at station 0
T=3110.0! finished j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3100, sp: 0) at station 0
T=3110.0! station=1, 1 jobs queued
T=3110.0! start j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: F, st: 3110, sp: 1) at station 1
T=3117.0! finished j(id: 4, p: 0, am: 1, ar: 3100, me: T, c: T, st: 3110, sp: 1) at station 1
T=3117.0! product=0, amount=1, in_warehouse=10, in_production=0, 0 pending demands
T=3117.0! 11 units of product 0 in warehouse
T=4000.0! product=1, amount=0, in_warehouse=21, in_production=0, 1 pending demands
T=4000.0! d(id: 3, p: 1, c: 3, am: 1, ar: 4000, dl: 5000, me: T) statisfied
T=4000.0! 20 units of product 1 in warehouse
T=4000.0! station=1, 1 jobs queued
T=4000.0! start j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4000, sp: 0) at station 1
T=4003.0! finished j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4000, sp: 0) at station 1
T=4003.0! station=0, 1 jobs queued
T=4003.0! start j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: F, st: 4003, sp: 1) at station 0
T=4008.0! finished j(id: 5, p: 1, am: 1, ar: 4000, me: T, c: T, st: 4003, sp: 1) at station 0
T=4008.0! product=1, amount=1, in_warehouse=20, in_production=0, 0 pending demands
T=4008.0! 21 units of product 1 in warehouse
T=4008.0 -- finished
>>> instance = Instance(
...     name="test2", n_products=2, n_customers=1, n_stations=2, n_demands=5,
...     time_end_warmup=21, time_end_measure=10000,
...     routes=[[0, 1], [1, 0]],
...     demands=[[0, 0, 1, 4, 20, 90], [1, 0, 0, 5, 22, 200],
...              [2, 0, 1, 4, 30, 200], [3, 0, 1, 6, 60, 200],
...              [4, 0, 0, 3, 5, 2000]],
...     warehous_at_t0=[2, 1],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> rop_sim = ROPSimulation(instance, PrintingListener(print_time=False))
>>> rop_sim.set_rop((10, 10))
>>> rop_sim.ctrl_run()
start
T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 2 units of product 0 in warehouse
T=0.0: station=0, 1 jobs queued
T=0.0: start j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 0, sp: 0) at station 0
T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 1 units of product 1 in warehouse
T=0.0: station=1, 1 jobs queued
T=0.0: start j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 0, sp: 0) at station 1
T=5.0: product=0, amount=0, in_warehouse=2, in_production=9, 1 pending demands
T=20.0: product=1, amount=0, in_warehouse=1, in_production=10, 1 pending demands
T=22.0! product=0, amount=0, in_warehouse=2, in_production=12, 2 pending demands
T=30.0! product=1, amount=0, in_warehouse=1, in_production=14, 2 pending demands
T=39.0: finished j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 0, sp: 0) at station 1
T=39.0! station=1, 2 jobs queued
T=39.0: start j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=57.0: finished j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=57.0! station=1, 1 jobs queued
T=57.0! start j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=60.0! product=1, amount=0, in_warehouse=1, in_production=18, 3 pending demands
T=69.0! finished j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=69.0! station=1, 1 jobs queued
T=69.0! start j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 60, sp: 0) at station 1
T=102.0! finished j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 60, sp: 0) at station 1
T=110.0: finished j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 0, sp: 0) at station 0
T=110.0! station=0, 6 jobs queued
T=110.0: start j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 5, sp: 0) at station 0
T=110.0! station=1, 1 jobs queued
T=110.0: start j(id: 0, p: 0, am: 9, ar: 0, me: F, c: F, st: 110, sp: 1) at station 1
T=140.0: finished j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 5, sp: 0) at station 0
T=140.0! station=0, 5 jobs queued
T=140.0! start j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=171.0: finished j(id: 0, p: 0, am: 9, ar: 0, me: F, c: T, st: 110, sp: 1) at station 1
T=171.0! station=1, 1 jobs queued
T=171.0: start j(id: 2, p: 0, am: 3, ar: 5, me: F, c: F, st: 140, sp: 1) at station 1
T=171.0! product=0, amount=9, in_warehouse=2, in_production=8, 2 pending demands
T=171.0: d(id: 4, p: 0, c: 0, am: 3, ar: 5, dl: 2000, me: F) statisfied
T=171.0! d(id: 1, p: 0, c: 0, am: 5, ar: 22, dl: 200, me: T) statisfied
T=171.0! 3 units of product 0 in warehouse
T=186.0: finished j(id: 2, p: 0, am: 3, ar: 5, me: F, c: T, st: 140, sp: 1) at station 1
T=186.0! product=0, amount=3, in_warehouse=3, in_production=5, 0 pending demands
T=186.0! 6 units of product 0 in warehouse
T=210.0! finished j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=210.0! station=0, 4 jobs queued
T=210.0: start j(id: 1, p: 1, am: 10, ar: 0, me: F, c: F, st: 39, sp: 1) at station 0
T=210.0! station=1, 1 jobs queued
T=210.0! start j(id: 4, p: 0, am: 5, ar: 22, me: T, c: F, st: 210, sp: 1) at station 1
T=245.0! finished j(id: 4, p: 0, am: 5, ar: 22, me: T, c: T, st: 210, sp: 1) at station 1
T=245.0! product=0, amount=5, in_warehouse=6, in_production=0, 0 pending demands
T=245.0! 11 units of product 0 in warehouse
T=263.0: finished j(id: 1, p: 1, am: 10, ar: 0, me: F, c: T, st: 39, sp: 1) at station 0
T=263.0! station=0, 3 jobs queued
T=263.0: start j(id: 3, p: 1, am: 4, ar: 20, me: F, c: F, st: 57, sp: 1) at station 0
T=263.0! product=1, amount=10, in_warehouse=1, in_production=14, 3 pending demands
T=263.0: d(id: 0, p: 1, c: 0, am: 4, ar: 20, dl: 90, me: F) statisfied
T=263.0! d(id: 2, p: 1, c: 0, am: 4, ar: 30, dl: 200, me: T) statisfied
T=263.0! 3 units of product 1 in warehouse
T=287.0: finished j(id: 3, p: 1, am: 4, ar: 20, me: F, c: T, st: 57, sp: 1) at station 0
T=287.0! station=0, 2 jobs queued
T=287.0! start j(id: 5, p: 1, am: 4, ar: 30, me: T, c: F, st: 69, sp: 1) at station 0
T=287.0! product=1, amount=4, in_warehouse=3, in_production=10, 1 pending demands
T=287.0! d(id: 3, p: 1, c: 0, am: 6, ar: 60, dl: 200, me: T) statisfied
T=287.0! 1 units of product 1 in warehouse
T=303.0! finished j(id: 5, p: 1, am: 4, ar: 30, me: T, c: T, st: 69, sp: 1) at station 0
T=303.0! station=0, 1 jobs queued
T=303.0! start j(id: 6, p: 1, am: 6, ar: 60, me: T, c: F, st: 102, sp: 1) at station 0
T=303.0! product=1, amount=4, in_warehouse=1, in_production=6, 0 pending demands
T=303.0! 5 units of product 1 in warehouse
T=337.0! finished j(id: 6, p: 1, am: 6, ar: 60, me: T, c: T, st: 102, sp: 1) at station 0
T=337.0! product=1, amount=6, in_warehouse=5, in_production=0, 0 pending demands
T=337.0! 11 units of product 1 in warehouse
T=337.0 -- finished
class moptipyapps.prodsched.rop_simulation.ROPSimulation(instance, listener)[source]

Bases: Simulation

Create the re-order point-based simulation.

event_product(time, product_id, amount, in_warehouse, in_production, pending_demands)[source]

Perform a simulation step based on a product.

Return type:

None

set_rop(rop)[source]

Set the re-order point.

Parameters:

rop (Union[Iterable[int], ndarray]) – the re-order point

Return type:

None

moptipyapps.prodsched.simulation module

A simulator for production scheduling.

For simulation a production system, we can build on the class Simulation. This base class offers the support to implement almost arbitrarily complex production system scheduling logic. The simulations here a fully deterministic and execute a given MFC scenario given as an Instance.

Simulations have three groups of methods:

  • Methods starting with ctrl_* are for starting and resetting the simulation so that it can be started again. You may override them if you have additional need for initialization or clean-up.

  • Methods that start with event_* are methods that are invoked by the simulator to notify you about an event in the simulation. You can overwrite these methods to implement the logic of your production scheduling method.

  • Methods that start with act_* are actions that you can invoke inside the event_* methods. The tell the simulator or stations what to do.

An example of such specialized simulations is the ROPSimulation, which simulates the behavior of a system that uses re-order points (ROPs) to decide what to produce and when. In such a simulation, the event_*-methods are overwritten to invoke the act_*-methods according to their needs. Here, in the base class Simulation, they are implemented such to order the production of product units directly upon the arrival of customer demands. In the ROPSimulation on the other hand, products are produced base on re-order points.

## We have the following ctrl_* methods:**

  • ctrl_run() runs the simulation.

  • ctrl_reset() resets the simulator so that we can start it again. If you want to re-use a simulation, you need to first invoke ctrl_reset() to clear the internal state.

## We have the following event_* methods:**

  • event_product() is invoked by the simulation if one of the following three things happened:

    1. An amount of a product has been produced (amount > 0).

    2. An amount of a product has been made available at the start of the simulation to form the initial amount in the warehouse (amount > 0).

    3. A customer demand for the product has appeared in the system.

    In this method, you can store product into the warehouse, remove product from the warehouse, and/or mark a demand as completed.

## Examples

Here we have a very easy production scheduling instance. There is 1 product that passes through 2 stations. First it passes through station 0, then through station 1. The per-unit production time is always 10 time units on station 0 and 30 time units on station 2. There is one customer demand, for 10 units of this product, which enters the system at time unit 20. The warehouse is initially empty.

>>> instance = Instance(
...     name="test1", n_products=1, n_customers=1, n_stations=2, n_demands=1,
...     time_end_warmup=10, time_end_measure=4000,
...     routes=[[0, 1]],
...     demands=[[0, 0, 0, 10, 20, 100]],
...     warehous_at_t0=[0],
...     station_product_unit_times=[[[10.0, 10000.0]],
...                                 [[30.0, 10000.0]]])

The simulation will see that the customer demand for 10 units of product 0 appears at time unit 20. It will issue a production order for these 10 units at station 0. Since station 0 is not occupied, it can immediately begin with the production. It will finish the production after 10*10 time units, i.e., at time unit 120. The product is then routed to station 1, which is also idle and can immediately begin producing. It needs 10*30 time units, meaning that it finishes after 300 time units. The demanded product amount is completed after 420 time units and the demand 0 can be fulfilled.

>>> simulation = Simulation(instance, PrintingListener(print_time=False))
>>> simulation.ctrl_run()
start
T=0.0: product=0, amount=0, in_warehouse=0, in_production=0, 0 pending demands
T=20.0! product=0, amount=0, in_warehouse=0, in_production=0, 1 pending demands
T=20.0! station=0, 1 jobs queued
T=20.0! start j(id: 0, p: 0, am: 10, ar: 20, me: T, c: F, st: 20, sp: 0) at station 0
T=120.0! finished j(id: 0, p: 0, am: 10, ar: 20, me: T, c: F, st: 20, sp: 0) at station 0
T=120.0! station=1, 1 jobs queued
T=120.0! start j(id: 0, p: 0, am: 10, ar: 20, me: T, c: F, st: 120, sp: 1) at station 1
T=420.0! finished j(id: 0, p: 0, am: 10, ar: 20, me: T, c: T, st: 120, sp: 1) at station 1
T=420.0! product=0, amount=10, in_warehouse=0, in_production=0, 1 pending demands
T=420.0! d(id: 0, p: 0, c: 0, am: 10, ar: 20, dl: 100, me: T) statisfied
T=420.0 -- finished
>>> instance = Instance(
...     name="test2", n_products=2, n_customers=1, n_stations=2, n_demands=3,
...     time_end_warmup=21, time_end_measure=10000,
...     routes=[[0, 1], [1, 0]],
...     demands=[[0, 0, 1, 10, 20, 90], [1, 0, 0, 5, 22, 200],
...              [2, 0, 1, 7, 30, 200]],
...     warehous_at_t0=[2, 1],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> instance.name
'test2'
>>> simulation = Simulation(instance, PrintingListener(print_time=False))
>>> simulation.ctrl_run()
start
T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 2 units of product 0 in warehouse
T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 1 units of product 1 in warehouse
T=20.0: product=1, amount=0, in_warehouse=1, in_production=0, 1 pending demands
T=20.0: station=1, 1 jobs queued
T=20.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=22.0! product=0, amount=0, in_warehouse=2, in_production=0, 1 pending demands
T=22.0! station=0, 1 jobs queued
T=22.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=30.0! product=1, amount=0, in_warehouse=1, in_production=9, 2 pending demands
T=52.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=62.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=62.0! station=1, 2 jobs queued
T=62.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=62.0! station=0, 1 jobs queued
T=62.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 62, sp: 1) at station 0
T=95.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=95.0! station=1, 1 jobs queued
T=95.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 52, sp: 1) at station 1
T=107.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: T, st: 62, sp: 1) at station 0
T=107.0! station=0, 1 jobs queued
T=107.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 95, sp: 1) at station 0
T=107.0! product=1, amount=9, in_warehouse=1, in_production=7, 2 pending demands
T=107.0: d(id: 0, p: 1, c: 0, am: 10, ar: 20, dl: 90, me: F) statisfied
T=107.0! 0 units of product 1 in warehouse
T=112.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: T, st: 52, sp: 1) at station 1
T=112.0! product=0, amount=3, in_warehouse=2, in_production=0, 1 pending demands
T=112.0! d(id: 1, p: 0, c: 0, am: 5, ar: 22, dl: 200, me: T) statisfied
T=112.0! 0 units of product 0 in warehouse
T=144.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: T, st: 95, sp: 1) at station 0
T=144.0! product=1, amount=7, in_warehouse=0, in_production=0, 1 pending demands
T=144.0! d(id: 2, p: 1, c: 0, am: 7, ar: 30, dl: 200, me: T) statisfied
T=144.0 -- finished
>>> simulation.ctrl_reset()
>>> simulation.ctrl_run()
start
T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 2 units of product 0 in warehouse
T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 1 units of product 1 in warehouse
T=20.0: product=1, amount=0, in_warehouse=1, in_production=0, 1 pending demands
T=20.0: station=1, 1 jobs queued
T=20.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=22.0! product=0, amount=0, in_warehouse=2, in_production=0, 1 pending demands
T=22.0! station=0, 1 jobs queued
T=22.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=30.0! product=1, amount=0, in_warehouse=1, in_production=9, 2 pending demands
T=52.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=62.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=62.0! station=1, 2 jobs queued
T=62.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=62.0! station=0, 1 jobs queued
T=62.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 62, sp: 1) at station 0
T=95.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=95.0! station=1, 1 jobs queued
T=95.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 52, sp: 1) at station 1
T=107.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: T, st: 62, sp: 1) at station 0
T=107.0! station=0, 1 jobs queued
T=107.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 95, sp: 1) at station 0
T=107.0! product=1, amount=9, in_warehouse=1, in_production=7, 2 pending demands
T=107.0: d(id: 0, p: 1, c: 0, am: 10, ar: 20, dl: 90, me: F) statisfied
T=107.0! 0 units of product 1 in warehouse
T=112.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: T, st: 52, sp: 1) at station 1
T=112.0! product=0, amount=3, in_warehouse=2, in_production=0, 1 pending demands
T=112.0! d(id: 1, p: 0, c: 0, am: 5, ar: 22, dl: 200, me: T) statisfied
T=112.0! 0 units of product 0 in warehouse
T=144.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: T, st: 95, sp: 1) at station 0
T=144.0! product=1, amount=7, in_warehouse=0, in_production=0, 1 pending demands
T=144.0! d(id: 2, p: 1, c: 0, am: 7, ar: 30, dl: 200, me: T) statisfied
T=144.0 -- finished

Now we want to stop the simulation measurement period before the last job completes. Notice that the last production jobs after time unit 81.xxx are no longer performed, because their end falls outside of the measurement period.

>>> instance = Instance(
...     name="test3", n_products=2, n_customers=1, n_stations=2, n_demands=3,
...     time_end_warmup=21, time_end_measure=100,
...     routes=[[0, 1], [1, 0]],
...     demands=[[0, 0, 1, 10, 20, 90], [1, 0, 0, 5, 22, 200],
...              [2, 0, 1, 7, 30, 200]],
...     warehous_at_t0=[2, 1],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> instance.name
'test3'
>>> simulation = Simulation(instance, PrintingListener(print_time=False))
>>> simulation.ctrl_run()
start
T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 2 units of product 0 in warehouse
T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 1 units of product 1 in warehouse
T=20.0: product=1, amount=0, in_warehouse=1, in_production=0, 1 pending demands
T=20.0: station=1, 1 jobs queued
T=20.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=22.0! product=0, amount=0, in_warehouse=2, in_production=0, 1 pending demands
T=22.0! station=0, 1 jobs queued
T=22.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=30.0! product=1, amount=0, in_warehouse=1, in_production=9, 2 pending demands
T=52.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=62.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=62.0! station=1, 2 jobs queued
T=62.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=62.0! station=0, 1 jobs queued
T=62.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 62, sp: 1) at station 0
T=95.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=95.0! station=1, 1 jobs queued
T=95.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 52, sp: 1) at station 1
T=95.0 -- finished
>>> simulation.ctrl_reset()
>>> simulation.ctrl_run()
start
T=0.0: product=0, amount=2, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 2 units of product 0 in warehouse
T=0.0: product=1, amount=1, in_warehouse=0, in_production=0, 0 pending demands
T=0.0: 1 units of product 1 in warehouse
T=20.0: product=1, amount=0, in_warehouse=1, in_production=0, 1 pending demands
T=20.0: station=1, 1 jobs queued
T=20.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=22.0! product=0, amount=0, in_warehouse=2, in_production=0, 1 pending demands
T=22.0! station=0, 1 jobs queued
T=22.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=30.0! product=1, amount=0, in_warehouse=1, in_production=9, 2 pending demands
T=52.0! finished j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 22, sp: 0) at station 0
T=62.0: finished j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 20, sp: 0) at station 1
T=62.0! station=1, 2 jobs queued
T=62.0! start j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=62.0! station=0, 1 jobs queued
T=62.0: start j(id: 0, p: 1, am: 9, ar: 20, me: F, c: F, st: 62, sp: 1) at station 0
T=95.0! finished j(id: 2, p: 1, am: 7, ar: 30, me: T, c: F, st: 30, sp: 0) at station 1
T=95.0! station=1, 1 jobs queued
T=95.0! start j(id: 1, p: 0, am: 3, ar: 22, me: T, c: F, st: 52, sp: 1) at station 1
T=95.0 -- finished
class moptipyapps.prodsched.simulation.Job(job_id, product_id, amount, arrival, measure, completed=False, station_time=-1.0, step=-1)[source]

Bases: object

The record for a production job.

amount: int

the amount to produce

arrival: float

the time when the job was issued

completed: bool = False

is the job completed?

job_id: int

the unique job id

measure: bool

should the job be considered during measurement?

product_id: int

the ID of the product to be produced.

station_time: float = -1.0

the time when the job arrived at the queue of the current station.

step: int = -1

the current job step, starts at 0.

class moptipyapps.prodsched.simulation.Listener[source]

Bases: object

A listener for simulation events.

demand_satisfied(time, demand)[source]

Report that a given demand has been satisfied.

Parameters:
  • time (float) – the time index when the demand was satisfied

  • demand (Demand) – the demand that was satisfied

Return type:

None

event_product(time, product_id, amount, in_warehouse, in_production, pending_demands, is_in_measure_period)[source]

Get notified right before Simulation.event_product().

Parameters:
  • time (float) – the current system time

  • product_id (int) – the id of the product

  • amount (int) – the amount of the product that appears

  • in_warehouse (int) – the amount of the product currently in the warehouse

  • in_production (int) – the amounf of product currently under production

  • pending_demands (tuple[Demand, ...]) – the pending orders for the product

  • is_in_measure_period (bool) – is this event inside the measurement period?

Return type:

None

event_station(time, station_id, queue, is_in_measure_period)[source]

Get notified right before Simulation.event_station().

If this event happens, the station is not busy. It could process a job and there is at least one job that it could process. You can now select the job to be executed from the queue and pass it to act_exec_job().

Parameters:
  • time (float) – the current time

  • station_id (int) – the station ID

  • queue (tuple[Job, ...]) – the job queue for this station

  • is_in_measure_period (bool) – is this event inside the measurement period?

Return type:

None

finished(time)[source]

Be notified that the simulation has been finished.

Parameters:

time (float) – the time when we are finished

Return type:

None

produce_at_begin(time, station_id, job)[source]

Report the start of the production of a certain product at a station.

Parameters:
  • time (float) – the current time

  • station_id (int) – the station ID

  • job (Job) – the production job

Return type:

None

produce_at_end(time, station_id, job)[source]

Report the completion of the production of a product at a station.

Parameters:
  • time (float) – the current time

  • station_id (int) – the station ID

  • job (Job) – the production job

Return type:

None

product_in_warehouse(time, product_id, amount, is_in_measure_period)[source]

Report a change of the amount of products in the warehouse.

Parameters:
  • time (float) – the current time

  • product_id (int) – the product ID

  • amount (int) – the new absolute total amount of that product in the warehouse

  • is_in_measure_period (bool) – is this event inside the measurement period?

Return type:

None

start()[source]

Get notification that the simulation is starting.

Return type:

None

class moptipyapps.prodsched.simulation.PrintingListener(output=<built-in function print>, print_time=True)[source]

Bases: Listener

A listener that just prints simulation events.

demand_satisfied(time, demand)[source]

Print that a demand was satisfied.

Return type:

None

event_product(time, product_id, amount, in_warehouse, in_production, pending_demands, is_in_measure_period)[source]

Print the prouct event.

Return type:

None

event_station(time, station_id, queue, is_in_measure_period)[source]

Print the station event.

Return type:

None

finished(time)[source]

Print that the simulation has finished.

Return type:

None

produce_at_begin(time, station_id, job)[source]

Print that the production at a given station begun.

Return type:

None

produce_at_end(time, station_id, job)[source]

Print that the production at a given station ended.

Return type:

None

product_in_warehouse(time, product_id, amount, is_in_measure_period)[source]

Print the product amount in the warehouse.

Return type:

None

start()[source]

Print that the simulation begins.

Return type:

None

class moptipyapps.prodsched.simulation.Simulation(instance, listener)[source]

Bases: object

A simulator for production scheduling.

act_demand_satisfied(demand)[source]

Notify the system that a given demand has been satisfied.

Parameters:

demand (Demand) – the demand that was satisfied

Return type:

None

act_exec_job(job)[source]

Execute the job on its current station.

Parameters:

job (Job) – the job to be executed

Return type:

None

act_produce(product_id, amount)[source]

Order the production of amount units of product.

Parameters:
  • product_id (int) – the product ID

  • amount (int) – the amount that needs to be produced

Return type:

None

act_store_in_warehouse(product_id, amount)[source]

Add a certain amount of product to the warehouse.

Parameters:
  • product_id (int) – the product ID

  • amount (int) – the amount

Return type:

None

act_take_from_warehouse(product_id, amount)[source]

Remove a certain amount of product to the warehouse.

Parameters:
  • product_id (int) – the product ID

  • amount (int) – the amount

Return type:

None

ctrl_reset()[source]

Reset the simulation.

This function sets the time to 0, clears the event queue, clears the pending orders list, clears the warehouse.

Return type:

None

ctrl_run()[source]

Run the simulation.

This function executes the main loop of the simulation. It runs the central event pump, which is a priority queue. It processes the simulation events one by one.

Return type:

None

event_product(time, product_id, amount, in_warehouse, in_production, pending_demands)[source]

Take actions when an event regarding a product or demand occurred.

The following events may have occurred:

  1. An amount of a product has been produced (amount > 0).

  2. An amount of a product has been made available at the start of the simulation to form the initial amount in the warehouse (amount > 0).

  3. A customer demand for the product has appeared in the system. If there is any demand to be fulfilled, then pending_demands is not empty.

You can choose to execute one or multiple of the following actions:

  1. act_store_in_warehouse() to store a positive amount of product in the warehouse.

  2. act_take_from_warehouse() to take a positive amount of product out of the warehouse (must be <= in_warehouse.

  3. act_produce() to order the production of a positive amount of the product.

  4. act_demand_satisfied() to mark one of the demands from queue as satisfied. Notice that in this case, you must make sure to remove the corresponding amount of product units from the system. If sufficient units are in amount, you would simply not store these in the warehouse. You could also simply take some units out of the warehouse with act_take_from_warehouse().

Parameters:
  • time (float) – the current system time

  • product_id (int) – the id of the product

  • amount (int) – the amount of the product that appears

  • in_warehouse (int) – the amount of the product currently in the warehouse

  • in_production (int) – the amounf of product currently under production

  • pending_demands (tuple[Demand, ...]) – the pending orders for the product

Return type:

None

event_station(time, station_id, queue)[source]

Process an event for a given station.

If this event happens, the station is not busy. It could process a job and there is at least one job that it could process. You can now select the job to be executed from the queue and pass it to act_exec_job().

Parameters:
  • time (float) – the current time

  • station_id (int) – the station ID

  • queue (tuple[Job, ...]) – the job queue for this station

Return type:

None

instance: Final[Instance]

the instance whose data is simulated

moptipyapps.prodsched.simulation.warmup()[source]

Perform a warm-up for our simulator.

The simulator uses some code implemented in numba etc., which may need to be jitted before the actual execution.

Return type:

None

>>> warmup()

moptipyapps.prodsched.statistics module

A statistics record for the simulation.

This module provides a record with statistics derived from one single MFC simulation. It can store values such as the mean fill rate or the mean stock level.

moptipyapps.prodsched.statistics.COL_PRODUCT_PREFIX: Final[str] = 'product_'

the product column prefix

moptipyapps.prodsched.statistics.COL_STAT: Final[str] = 'stat'

the name of the statistics key

moptipyapps.prodsched.statistics.COL_TOTAL: Final[str] = 'total'

the total column name

moptipyapps.prodsched.statistics.KEY_RATE: Final[str] = 'rate'

the statistics rate

moptipyapps.prodsched.statistics.ROW_CWT: Final[str] = 'cwt'

the CWT row

moptipyapps.prodsched.statistics.ROW_FILL_RATE: Final[str] = 'fill.rate'

the fill rate row

moptipyapps.prodsched.statistics.ROW_FULFILLED_RATE: Final[str] = 'fulfilled.rate'

the fulfilled rate

moptipyapps.prodsched.statistics.ROW_SIMULATION_TIME: Final[str] = 'time.s: '

the simulation time getter

moptipyapps.prodsched.statistics.ROW_STOCK_LEVEL_MEAN: Final[str] = 'stocklevel.mean'

the mean stock level row

moptipyapps.prodsched.statistics.ROW_TRP: Final[str] = 'trp'

the mean TRP row

class moptipyapps.prodsched.statistics.Statistics(n_products)[source]

Bases: object

A statistics record based on production scheduling.

copy_from(stat)[source]

Copy the contents of another statistics record.

Parameters:

stat (Statistics) – the other statistics record

Return type:

None

fulfilled_rate: int | float | None

the fraction of demands that were fulfilled overall

fulfilled_rates: Final[list[int | float | None]]

the fraction of demands that were fulfilled, on a per-product basis

immediate_rate: int | float | None

the overall fraction of immediately satisfied demands

immediate_rates: Final[list[int | float | None]]

the fraction of demands that were immediately satisfied, on a per-product basis

production_time: StreamStatistics | None

the overall production time statistics

production_times: Final[list[StreamStatistics | None]]

the production time statistics per-product

simulation_time_nanos: int | float | None

the nano seconds used by the simulation

stock_level: int | float | None

the overall average stock level

stock_levels: Final[list[int | float | None]]

the average stock level, on a per-product basis

waiting_time: StreamStatistics | None

the overall waiting time for all demands that were not immediately satisfied – only counting demands that were actually satisfied

waiting_times: Final[list[StreamStatistics | None]]

the average waiting time for all demands that were not immediately satisfied – only counting demands that were actually satisfied

moptipyapps.prodsched.statistics.to_stream(stats)[source]

Write a statistics record to a stream.

Parameters:

stats (Statistics) – the statistics record

Return type:

Generator[str, None, None]

Returns:

the stream of data

moptipyapps.prodsched.statistics_collector module

A tool for collecting statistics from an MFC simulation.

A statistics collector is a special Listener that can be plugged into a Simulation. During the execution of the simulation, it gathers statistics about what is going on. It finally stores these into a Statistics record. Such a record can then be used to understand the key characteristics of the behavior of the simulation in on a given Instance.

>>> instance = Instance(
...     name="test2", n_products=2, n_customers=1, n_stations=2, n_demands=5,
...     time_end_warmup=21, time_end_measure=10000,
...     routes=[[0, 1], [1, 0]],
...     demands=[[0, 0, 1, 10, 20, 90], [1, 0, 0, 5, 22, 200],
...              [2, 0, 1, 7, 30, 200], [3, 0, 1, 6, 60, 200],
...              [4, 0, 0, 125, 5, 2000]],
...     warehous_at_t0=[2, 1],
...     station_product_unit_times=[[[10.0, 50.0, 15.0, 100.0],
...                                  [ 5.0, 20.0,  7.0,  35.0, 4.0, 50.0]],
...                                 [[ 5.0, 24.0,  7.0,  80.0],
...                                  [ 3.0, 21.0,  6.0,  50.0,]]])
>>> from moptipyapps.prodsched.simulation import Simulation
>>> statistics = Statistics(instance.n_products)
>>> collector = StatisticsCollector(instance)
>>> collector.set_dest(statistics)
>>> simulation = Simulation(instance, collector)
>>> simulation.ctrl_run()
>>> print("\n".join(str(statistics).split("\n")[:-1]))
stat;total;product_0;product_1
trp.min;229.57142857142858;455.6;229.57142857142858
trp.mean;304.8888888888889;455.6;246.92307692307693
trp.max;455.6;455.6;267.1666666666667
trp.sd;97.56326157594913;0;19.50721118346466
cwt.min;1603;2278;1603
cwt.mean;1829.3333333333333;2278;1605
cwt.max;2278;2278;1607
cwt.sd;388.56187838403986;;2.8284271247461903
fill.rate;0;0;0
stocklevel.mean;0.6078765407355446;0.4497444633730835;0.15813207736246118
fulfilled.rate;1;1;1
class moptipyapps.prodsched.statistics_collector.StatisticsCollector(instance)[source]

Bases: Listener

A listener for simulation events.

demand_satisfied(time, demand)[source]

Report that a given demand has been satisfied.

Parameters:
  • time (float) – the time index when the demand was satisfied

  • demand (Demand) – the demand that was satisfied

Return type:

None

finished(time)[source]

Fill the collected statistics into the statistics record.

Parameters:

time (float) – the time when we are finished

Return type:

None

produce_at_end(time, station_id, job)[source]

Report the completion of the production of a product at a station.

Parameters:
  • time (float) – the current time

  • station_id (int) – the station ID

  • job (Job) – the production job

Return type:

None

product_in_warehouse(time, product_id, amount, is_in_measure_period)[source]

Report a change of the amount of products in the warehouse.

Parameters:
  • time (float) – the current time

  • product_id (int) – the product ID

  • amount (int) – the new absolute total amount of that product in the warehouse

  • is_in_measure_period (bool) – is this event inside the measurement period?

Return type:

None

set_dest(dest)[source]

Set the statistics record to fill.

Parameters:

dest (Statistics) – the destination

Return type:

None

start()[source]

Clear all the data of the collector.

Return type:

None