Tutorial 03 - Generating New Problems (Random Instance Generation)¶
This notebook shows how to generate random JobShopInstance objects using the function modular_instance_generator.
Deprecated: The classes
InstanceGeneratorandGeneralInstanceGeneratorare deprecated. Usemodular_instance_generatorinstead.
Why a Modular Function?¶
The design emphasizes two key patterns:
Dependency Injection – You inject callables that build each matrix (machine routing, durations, release dates, etc.) instead of the generator hard-coding logic. This increases testability and flexibility.
Strategy Pattern – Each callable acts as a strategy. Swap routing, duration, or release-date strategies independently without modifying the core generator.
The generator returns an infinite stream of instances. Use next() or slice it with itertools.islice.
Signature (Annotated)¶
modular_instance_generator(
machine_matrix_creator: Callable[[random.Random], MachineMatrix],
duration_matrix_creator: Callable[[MachineMatrix, random.Random], DurationMatrix],
*,
name_creator: Callable[[int], str] = lambda i: f"generated_instance_{i}",
release_dates_matrix_creator: Callable[[DurationMatrix, random.Random], ReleaseDatesMatrix] | None = None,
deadlines_matrix_creator: Callable[[DurationMatrix, random.Random], DeadlinesMatrix] | None = None,
due_dates_matrix_creator: Callable[[DurationMatrix, random.Random], DueDatesMatrix] | None = None,
seed: int | None = None,
) -> Generator[JobShopInstance, None, None]
Where each Matrix is a nested list (ragged lists allowed for jobs).
[ ]:
# Imports and basic setup
from itertools import islice
from job_shop_lib.generation import (
modular_instance_generator,
get_default_machine_matrix_creator,
get_default_duration_matrix_creator,
range_size_selector,
choice_size_selector,
create_release_dates_matrix,
get_mixed_release_date_strategy,
compute_horizon_proxy,
)
import random
# For deterministic examples
BASE_SEED = 42
Basic Example¶
Generate a fixed 3x3 instance without recirculation and durations in [1, 10].
[2]:
machine_creator = get_default_machine_matrix_creator(
size_selector=lambda rng: (3, 3),
with_recirculation=False,
)
duration_creator = get_default_duration_matrix_creator((1, 10))
gen_basic = modular_instance_generator(
machine_matrix_creator=machine_creator,
duration_matrix_creator=duration_creator,
seed=BASE_SEED,
)
inst = next(gen_basic)
inst, inst.duration_matrix_array
[2]:
(JobShopInstance(name=generated_instance_0, num_jobs=3, num_machines=3),
array([[ 5., 6., 4.],
[ 5., 7., 10.],
[ 9., 9., 5.]], dtype=float32))
Custom Naming Strategy¶
Provide a callable that maps the index to a formatted name.
[3]:
def fancy_name(i: int) -> str: # zero-padded index
return f"expA_seed{BASE_SEED}_{i:03d}"
gen_named = modular_instance_generator(
machine_matrix_creator=machine_creator,
duration_matrix_creator=duration_creator,
name_creator=fancy_name,
seed=BASE_SEED,
)
[next(gen_named).name for _ in range(3)]
[3]:
['expA_seed42_000', 'expA_seed42_001', 'expA_seed42_002']
Random Sizes (Size Selectors)¶
Use range-based or discrete-choice size selectors captured in the machine matrix creator.
[4]:
# Range-based size selection
machine_creator_range = get_default_machine_matrix_creator(
size_selector=lambda rng: range_size_selector(
rng,
num_jobs_range=(5, 7),
num_machines_range=(3, 5),
allow_less_jobs_than_machines=True,
),
with_recirculation=True,
)
# Choice-based size selection
OPTIONS = [(3, 3), (4, 5), (6, 4)]
machine_creator_choice = get_default_machine_matrix_creator(
size_selector=lambda rng: choice_size_selector(rng, OPTIONS),
with_recirculation=False,
)
duration_creator_var = get_default_duration_matrix_creator((2, 15))
gen_choice = modular_instance_generator(
machine_matrix_creator=machine_creator_choice,
duration_matrix_creator=duration_creator_var,
seed=7,
)
[next(gen_choice) for _ in range(2)]
[4]:
[JobShopInstance(name=generated_instance_0, num_jobs=4, num_machines=5),
JobShopInstance(name=generated_instance_1, num_jobs=6, num_machines=4)]
Adding Release Dates¶
You can wrap create_release_dates_matrix with a custom mixed strategy.
[ ]:
def release_dates_creator(duration_matrix, rng):
horizon = compute_horizon_proxy(duration_matrix)
strat = get_mixed_release_date_strategy(0.6, 0.4, horizon)
return create_release_dates_matrix(
duration_matrix, strategy=strat, rng=rng
)
gen_with_release = modular_instance_generator(
machine_matrix_creator=machine_creator,
duration_matrix_creator=duration_creator,
release_dates_matrix_creator=release_dates_creator,
seed=123,
)
inst_with_release = next(gen_with_release)
inst_with_release.release_dates_matrix
[[0, 6, 9], [0, 4, 7], [4, 5, 8]]
Deadlines & Due Dates¶
Provide simple heuristic strategies. You decide semantics.
[ ]:
def deadlines_creator(duration_matrix, rng):
deadlines = []
for job in duration_matrix:
cum = 0
row = []
for duration in job:
cum += duration
row.append(int(cum * 2)) # 100% slack
deadlines.append(row)
return deadlines
def due_dates_creator(duration_matrix, rng):
due_dates = []
for job in duration_matrix:
cum = 0
row = []
for duration in job:
cum += duration
row.append(cum + rng.randint(0, duration))
due_dates.append(row)
return due_dates
gen_with_all = modular_instance_generator(
machine_matrix_creator=machine_creator,
duration_matrix_creator=duration_creator,
release_dates_matrix_creator=release_dates_creator,
deadlines_matrix_creator=deadlines_creator,
due_dates_matrix_creator=due_dates_creator,
seed=99,
)
complex_instance = next(gen_with_all)
print(complex_instance)
print("complex_instance.due_dates_matrix_array =")
print(complex_instance.due_dates_matrix_array)
print("complex_instance.deadlines_matrix_array =")
print(complex_instance.deadlines_matrix_array)
print("complex_instance.release_dates_matrix_array =")
print(complex_instance.release_dates_matrix_array)
JobShopInstance(name=generated_instance_0, num_jobs=3, num_machines=3)
complex_instance.due_dates_matrix_array =
[[14. 9. 25.]
[ 4. 12. 14.]
[13. 18. 27.]]
complex_instance.deadlines_matrix_array =
[[14. 18. 38.]
[ 4. 18. 24.]
[14. 30. 50.]]
complex_instance.release_dates_matrix_array =
[[ 3. 6. 8.]
[ 3. 3. 6.]
[ 4. 10. 10.]]
Migration Example (Deprecated Class)¶
Old approach (for reference, no execution here):
# Deprecated
# gen = GeneralInstanceGenerator(num_jobs=(5,10), num_machines=(4,6), duration_range=(2,20))
# inst = gen.generate()
New modular approach:
[16]:
from functools import partial
size_selector = partial(
range_size_selector,
num_jobs_range=(5, 8),
num_machines_range=(4, 6),
allow_less_jobs_than_machines=False,
)
migrated_machine_creator = get_default_machine_matrix_creator(
size_selector=size_selector, with_recirculation=False
)
migrated_duration_creator = get_default_duration_matrix_creator((2, 20))
gen_migrated = modular_instance_generator(
migrated_machine_creator, migrated_duration_creator, seed=0
)
next(gen_migrated)
[16]:
JobShopInstance(name=generated_instance_0, num_jobs=8, num_machines=5)
Finite Sample Extraction¶
Use itertools.islice to limit the infinite generator.
[8]:
finite_instances = list(islice(gen_basic, 3)) # reuse earlier generator
len(finite_instances), [inst.name for inst in finite_instances]
[8]:
(3, ['generated_instance_1', 'generated_instance_2', 'generated_instance_3'])
Summary¶
modular_instance_generator provides a clean, composable way to build random Job Shop instances. You control routing, durations, and time-related constraints via strategy callables—simplifying experimentation and testing.
You can now integrate these instances into solvers, RL environments, or graph builders.