Skip to content

Commit d9bd9e6

Browse files
committed
Add a solve script for sanity check
1 parent d2ed97c commit d9bd9e6

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

solve.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# /// script
2+
# dependencies = [
3+
# "ortools",
4+
# "fjsplib",
5+
# ]
6+
# ///
7+
8+
import argparse
9+
from dataclasses import dataclass
10+
from pathlib import Path
11+
12+
from ortools.sat.python.cp_model import (
13+
BoolVarT,
14+
CpModel,
15+
CpSolver,
16+
IntervalVar,
17+
IntVar,
18+
)
19+
20+
from fjsplib import Instance, read
21+
22+
horizon = 2**32
23+
24+
25+
@dataclass
26+
class ModeVar:
27+
start: IntVar
28+
duration: IntVar
29+
end: IntVar
30+
present: BoolVarT
31+
interval: IntervalVar
32+
33+
34+
def build(instance: Instance) -> CpModel:
35+
"""
36+
Builds a CP model from an FJSPLIB instance.
37+
"""
38+
model = CpModel()
39+
40+
# Define operation variables.
41+
op_vars = []
42+
for _ in range(instance.num_operations):
43+
start = model.new_int_var(0, horizon, name="")
44+
duration = model.new_int_var(0, horizon, name="")
45+
end = model.new_int_var(0, horizon, name="")
46+
interval = model.new_interval_var(start, duration, end, name="")
47+
op_vars.append(interval)
48+
49+
# Define mode variables.
50+
mode_vars = {}
51+
op_idx = 0
52+
for job in instance.jobs:
53+
for operations in job:
54+
for machine, duration in operations:
55+
start = model.new_int_var(0, horizon, name="")
56+
duration = model.new_constant(duration)
57+
end = model.new_int_var(0, horizon, name="")
58+
present = model.new_bool_var(name="")
59+
interval = model.new_optional_interval_var(
60+
start, duration, end, present, name=""
61+
)
62+
var = ModeVar(start, duration, end, present, interval)
63+
mode_vars[op_idx, machine + 1] = var
64+
65+
op_idx += 1
66+
67+
# Select one mode and synchronize
68+
for operation in range(instance.num_operations):
69+
selected = []
70+
71+
for machine in range(instance.num_machines):
72+
if (operation, machine) in mode_vars:
73+
op_var = op_vars[operation]
74+
mode_var = mode_vars[operation, machine]
75+
selected.append(mode_var.present)
76+
77+
expr = op_var.start_expr() == mode_var.start
78+
model.add(expr)
79+
80+
expr = op_var.end_expr() == mode_var.end
81+
model.add(expr)
82+
83+
expr = op_var.size_expr() == mode_var.duration
84+
model.add(expr).only_enforce_if(mode_var.present)
85+
86+
model.add_exactly_one(selected)
87+
88+
# Define no overlap.
89+
for machine in range(instance.num_machines):
90+
intervals = [
91+
mode_vars[operation, machine].interval
92+
for operation in range(instance.num_operations)
93+
if (operation, machine) in mode_vars
94+
]
95+
model.add_no_overlap(intervals)
96+
97+
# Define precedence constraints.
98+
for pred, succ in instance.precedences:
99+
end = op_vars[pred].end_expr()
100+
start = op_vars[succ].start_expr()
101+
model.add(end <= start)
102+
103+
makespan = model.new_int_var(0, horizon, name="")
104+
ends = [var.end_expr() for var in op_vars]
105+
model.add_max_equality(makespan, ends)
106+
model.minimize(makespan)
107+
108+
return model
109+
110+
111+
def solve(model: CpModel, time_limit: int, display: bool, num_workers: int):
112+
"""
113+
Solves the CP model with a time limit.
114+
"""
115+
solver = CpSolver()
116+
solver.parameters.max_time_in_seconds = time_limit
117+
solver.parameters.log_search_progress = display
118+
solver.parameters.num_workers = num_workers
119+
120+
return solver.Solve(model)
121+
122+
123+
def parse_args():
124+
parser = argparse.ArgumentParser()
125+
126+
parser.add_argument("instance", type=Path)
127+
parser.add_argument("--time_limit", type=int, default=60)
128+
parser.add_argument("--display", action="store_true")
129+
parser.add_argument("--num_workers", type=int, default=0) # all cores
130+
131+
return parser.parse_args()
132+
133+
134+
if __name__ == "__main__":
135+
args = parse_args()
136+
instance = read(args.instance)
137+
model = build(instance)
138+
reuslt = solve(model, args.time_limit, args.display, args.num_workers)

0 commit comments

Comments
 (0)