Skip to content

Commit 1942e99

Browse files
committed
[Tests] Add comprehensive tests for objective functions and penalties
1 parent 70b61d8 commit 1942e99

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# pylint: disable=missing-function-docstring, redefined-outer-name
2+
import pytest
3+
4+
from job_shop_lib import (
5+
JobShopInstance,
6+
Operation,
7+
Schedule,
8+
ScheduledOperation,
9+
)
10+
from job_shop_lib.metaheuristics import (
11+
get_makespan_with_penalties_objective,
12+
compute_penalty_for_deadlines,
13+
compute_penalty_for_due_dates,
14+
)
15+
16+
17+
@pytest.fixture
18+
def schedule_no_penalties() -> Schedule:
19+
# Two machines; set due_date/deadline None
20+
jobs = [[Operation(0, 2)], [Operation(1, 3)]]
21+
instance = JobShopInstance(jobs, name="NoPenalties")
22+
# Build schedule manually: M0: job0@t0..2; M1: job1@t0..3
23+
s0 = ScheduledOperation(instance.jobs[0][0], start_time=0, machine_id=0)
24+
s1 = ScheduledOperation(instance.jobs[1][0], start_time=0, machine_id=1)
25+
schedule = Schedule(instance, [[s0], [s1]])
26+
return schedule
27+
28+
29+
@pytest.fixture
30+
def schedule_with_deadlines() -> Schedule:
31+
# Single machine sequence; second op violates deadline
32+
jobs = [
33+
[Operation(0, 2, deadline=1)], # ends at 2 -> violation
34+
[Operation(0, 3, deadline=5)], # ends at 5 -> boundary, no violation
35+
]
36+
instance = JobShopInstance(jobs, name="Deadlines")
37+
s0 = ScheduledOperation(instance.jobs[0][0], start_time=0, machine_id=0)
38+
s1 = ScheduledOperation(instance.jobs[1][0], start_time=2, machine_id=0)
39+
schedule = Schedule(instance, [[s0, s1]])
40+
return schedule
41+
42+
43+
@pytest.fixture
44+
def schedule_with_due_dates() -> Schedule:
45+
# Single machine sequence; first op OK, second violates due date
46+
jobs = [
47+
[Operation(0, 1, due_date=1)], # ends at 1 -> equal, OK
48+
[Operation(0, 4, due_date=3)], # ends at 5 -> violation
49+
]
50+
instance = JobShopInstance(jobs, name="DueDates")
51+
s0 = ScheduledOperation(instance.jobs[0][0], start_time=0, machine_id=0)
52+
s1 = ScheduledOperation(instance.jobs[1][0], start_time=1, machine_id=0)
53+
schedule = Schedule(instance, [[s0, s1]])
54+
return schedule
55+
56+
57+
@pytest.fixture
58+
def schedule_with_both() -> Schedule:
59+
# Mixed: first violates deadline, second violates due date
60+
jobs = [
61+
[Operation(0, 3, deadline=2, due_date=10)], # deadline violation
62+
[Operation(0, 4, deadline=10, due_date=6)], # due date violation
63+
]
64+
instance = JobShopInstance(jobs, name="Both")
65+
s0 = ScheduledOperation(instance.jobs[0][0], start_time=0, machine_id=0)
66+
s1 = ScheduledOperation(instance.jobs[1][0], start_time=3, machine_id=0)
67+
schedule = Schedule(instance, [[s0, s1]])
68+
return schedule
69+
70+
71+
def test_compute_penalty_for_deadlines_none(schedule_no_penalties: Schedule):
72+
assert compute_penalty_for_deadlines(schedule_no_penalties, 1000) == 0.0
73+
74+
75+
def test_compute_penalty_for_due_dates_none(schedule_no_penalties: Schedule):
76+
assert compute_penalty_for_due_dates(schedule_no_penalties, 100) == 0.0
77+
78+
79+
def test_compute_penalty_for_deadlines(schedule_with_deadlines: Schedule):
80+
# Only first op violates -> 1 penalty
81+
assert compute_penalty_for_deadlines(schedule_with_deadlines, 7.5) == 7.5
82+
83+
84+
def test_compute_penalty_for_due_dates(schedule_with_due_dates: Schedule):
85+
# Only second op violates -> 1 penalty
86+
assert compute_penalty_for_due_dates(schedule_with_due_dates, 3.0) == 3.0
87+
88+
89+
def test_objective_makespan_only_when_zero_factors(
90+
schedule_with_both: Schedule,
91+
):
92+
objective = get_makespan_with_penalties_objective(
93+
deadline_penalty_factor=0, due_date_penalty_factor=0
94+
)
95+
assert objective(schedule_with_both) == schedule_with_both.makespan()
96+
97+
98+
def test_objective_with_penalties(schedule_with_both: Schedule):
99+
# s0: 0..3 (violates deadline=2) -> +d_factor
100+
# s1: 3..7 (violates due_date=6) -> +dd_factor
101+
d_factor = 123.0
102+
dd_factor = 4.0
103+
objective = get_makespan_with_penalties_objective(
104+
deadline_penalty_factor=d_factor,
105+
due_date_penalty_factor=dd_factor,
106+
)
107+
expected = schedule_with_both.makespan() + d_factor + dd_factor
108+
assert objective(schedule_with_both) == expected

0 commit comments

Comments
 (0)