Skip to content

Commit 3cfe81d

Browse files
committed
polish example
1 parent 1ff8120 commit 3cfe81d

File tree

2 files changed

+37
-48
lines changed

2 files changed

+37
-48
lines changed

ortools/sat/docs/scheduling.md

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2676,7 +2676,6 @@ def sequence_constraints_with_circuit(
26762676
all_tasks = range(num_tasks)
26772677

26782678
arcs: List[cp_model.ArcT] = []
2679-
penalty_terms = []
26802679
for i in all_tasks:
26812680
# if node i is first.
26822681
start_lit = model.new_bool_var(f"start_{i}")
@@ -2696,17 +2695,6 @@ def sequence_constraints_with_circuit(
26962695
type_length_min = sequence_length_constraints[task_types[i]][0]
26972696
model.add(lengths[i] >= type_length_min).only_enforce_if(end_lit)
26982697

2699-
# Penalize the cumul of the last task w.r.t. the soft max
2700-
soft_max, linear_penalty, hard_max = sequence_cumul_constraints[task_types[i]]
2701-
if soft_max < hard_max:
2702-
aux = model.new_int_var(0, hard_max - soft_max, f"aux_{i}")
2703-
model.add_max_equality(aux, [0, cumuls[i] - soft_max])
2704-
2705-
excess = model.new_int_var(0, hard_max - soft_max, f"excess_{i}")
2706-
model.add(excess == aux).only_enforce_if(end_lit)
2707-
model.add(excess == 0).only_enforce_if(~end_lit)
2708-
penalty_terms.append((excess, linear_penalty))
2709-
27102698
for j in all_tasks:
27112699
if i == j:
27122700
continue
@@ -2746,19 +2734,25 @@ def sequence_constraints_with_circuit(
27462734
# Reset the cumul to the duration of the task.
27472735
model.add(cumuls[j] == durations[j]).only_enforce_if(lit)
27482736

2749-
# Penalize the cumul of the previous task w.r.t. the soft max
2750-
if soft_max < hard_max:
2751-
aux = model.new_int_var(0, hard_max - soft_max, f"aux_{i}")
2752-
model.add_max_equality(aux, [0, cumuls[i] - soft_max])
2753-
2754-
excess = model.new_int_var(0, hard_max - soft_max, f"excess_{i}")
2755-
model.add(excess == aux).only_enforce_if(lit)
2756-
model.add(excess == 0).only_enforce_if(~lit)
2757-
penalty_terms.append((excess, linear_penalty))
2758-
27592737
# Add the circuit constraint.
27602738
model.add_circuit(arcs)
27612739

2740+
# Create the penalty terms. We can penalize each cumul locally.
2741+
penalty_terms = []
2742+
for i in all_tasks:
2743+
# Penalize the cumul of the last task w.r.t. the soft max
2744+
soft_max, linear_penalty, hard_max = sequence_cumul_constraints[task_types[i]]
2745+
2746+
# To make it separable per task, and avoid double counting, we use the
2747+
# following trick:
2748+
# reduced_excess = min(durations[i], max(0, cumul[i] - soft_max))
2749+
if soft_max < hard_max:
2750+
excess = model.new_int_var(0, hard_max - soft_max, f"excess+_{i}")
2751+
model.add_max_equality(excess, [0, cumuls[i] - soft_max])
2752+
reduced_excess = model.new_int_var(0, durations[i], f"reduced_excess_{i}")
2753+
model.add_min_equality(reduced_excess, [durations[i], excess])
2754+
penalty_terms.append((reduced_excess, linear_penalty))
2755+
27622756
return penalty_terms
27632757

27642758

@@ -2826,13 +2820,13 @@ def sequences_in_no_overlap_sample_sat():
28262820
lengths = []
28272821
for i in all_tasks:
28282822
max_hard_length = sequence_length_constraints[task_types[i]][1]
2829-
lengths.append(model.new_int_var(0, max_hard_length, f"length_{i}"))
2823+
lengths.append(model.new_int_var(1, max_hard_length, f"length_{i}"))
28302824

28312825
# Create cumul variables for each task.
28322826
cumuls = []
28332827
for i in all_tasks:
28342828
max_hard_cumul = sequence_cumul_constraints[task_types[i]][2]
2835-
cumuls.append(model.new_int_var(0, max_hard_cumul, f"cumul_{i}"))
2829+
cumuls.append(model.new_int_var(durations[i], max_hard_cumul, f"cumul_{i}"))
28362830

28372831
# Adds NoOverlap constraint.
28382832
model.add_no_overlap(intervals)

ortools/sat/samples/sequences_in_no_overlap_sample_sat.py

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ def sequence_constraints_with_circuit(
6565
all_tasks = range(num_tasks)
6666

6767
arcs: List[cp_model.ArcT] = []
68-
penalty_terms = []
6968
for i in all_tasks:
7069
# if node i is first.
7170
start_lit = model.new_bool_var(f"start_{i}")
@@ -85,17 +84,6 @@ def sequence_constraints_with_circuit(
8584
type_length_min = sequence_length_constraints[task_types[i]][0]
8685
model.add(lengths[i] >= type_length_min).only_enforce_if(end_lit)
8786

88-
# Penalize the cumul of the last task w.r.t. the soft max
89-
soft_max, linear_penalty, hard_max = sequence_cumul_constraints[task_types[i]]
90-
if soft_max < hard_max:
91-
aux = model.new_int_var(0, hard_max - soft_max, f"aux_{i}")
92-
model.add_max_equality(aux, [0, cumuls[i] - soft_max])
93-
94-
excess = model.new_int_var(0, hard_max - soft_max, f"excess_{i}")
95-
model.add(excess == aux).only_enforce_if(end_lit)
96-
model.add(excess == 0).only_enforce_if(~end_lit)
97-
penalty_terms.append((excess, linear_penalty))
98-
9987
for j in all_tasks:
10088
if i == j:
10189
continue
@@ -135,19 +123,25 @@ def sequence_constraints_with_circuit(
135123
# Reset the cumul to the duration of the task.
136124
model.add(cumuls[j] == durations[j]).only_enforce_if(lit)
137125

138-
# Penalize the cumul of the previous task w.r.t. the soft max
139-
if soft_max < hard_max:
140-
aux = model.new_int_var(0, hard_max - soft_max, f"aux_{i}")
141-
model.add_max_equality(aux, [0, cumuls[i] - soft_max])
142-
143-
excess = model.new_int_var(0, hard_max - soft_max, f"excess_{i}")
144-
model.add(excess == aux).only_enforce_if(lit)
145-
model.add(excess == 0).only_enforce_if(~lit)
146-
penalty_terms.append((excess, linear_penalty))
147-
148126
# Add the circuit constraint.
149127
model.add_circuit(arcs)
150128

129+
# Create the penalty terms. We can penalize each cumul locally.
130+
penalty_terms = []
131+
for i in all_tasks:
132+
# Penalize the cumul of the last task w.r.t. the soft max
133+
soft_max, linear_penalty, hard_max = sequence_cumul_constraints[task_types[i]]
134+
135+
# To make it separable per task, and avoid double counting, we use the
136+
# following trick:
137+
# reduced_excess = min(durations[i], max(0, cumul[i] - soft_max))
138+
if soft_max < hard_max:
139+
excess = model.new_int_var(0, hard_max - soft_max, f"excess+_{i}")
140+
model.add_max_equality(excess, [0, cumuls[i] - soft_max])
141+
reduced_excess = model.new_int_var(0, durations[i], f"reduced_excess_{i}")
142+
model.add_min_equality(reduced_excess, [durations[i], excess])
143+
penalty_terms.append((reduced_excess, linear_penalty))
144+
151145
return penalty_terms
152146

153147

@@ -215,13 +209,13 @@ def sequences_in_no_overlap_sample_sat():
215209
lengths = []
216210
for i in all_tasks:
217211
max_hard_length = sequence_length_constraints[task_types[i]][1]
218-
lengths.append(model.new_int_var(0, max_hard_length, f"length_{i}"))
212+
lengths.append(model.new_int_var(1, max_hard_length, f"length_{i}"))
219213

220214
# Create cumul variables for each task.
221215
cumuls = []
222216
for i in all_tasks:
223217
max_hard_cumul = sequence_cumul_constraints[task_types[i]][2]
224-
cumuls.append(model.new_int_var(0, max_hard_cumul, f"cumul_{i}"))
218+
cumuls.append(model.new_int_var(durations[i], max_hard_cumul, f"cumul_{i}"))
225219

226220
# Adds NoOverlap constraint.
227221
model.add_no_overlap(intervals)
@@ -244,6 +238,7 @@ def sequences_in_no_overlap_sample_sat():
244238

245239
# Solves the model model.
246240
solver = cp_model.CpSolver()
241+
solver.parameters.log_search_progress = True
247242
status = solver.solve(model)
248243

249244
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:

0 commit comments

Comments
 (0)