1
+ using Test
2
+ using JuMP
3
+ using DisjunctiveProgramming
4
+
5
+ @testset " linear constraints" begin
6
+
7
+ function minimal ()
8
+ m = Model ()
9
+ @variable (m, - 1 <= x<= 10 )
10
+
11
+ @constraint (m, con1, x<= 3 )
12
+ @constraint (m, con2, 0 <= x)
13
+ @constraint (m, con3, x<= 9 )
14
+ @constraint (m, con4, 5 <= x)
15
+
16
+ @disjunction (m,(con1,con2),con3,con4,reformulation= :CHR ,name= :y )
17
+
18
+ @test true
19
+ end
20
+
21
+ function simple_example (reform)
22
+ m = Model ()
23
+ @variable (m, - 10 <= x<= 10 )
24
+ @constraint (m, con1, x<= - 1 )
25
+ @constraint (m, con2, 1 <= x)
26
+
27
+ if (reform == :BMR )
28
+ @disjunction (m, con1, con2, reformulation= :BMR , name= :y )
29
+ elseif (reform == :CHR )
30
+ @disjunction (m, con1, con2, reformulation= :CHR , name= :y )
31
+ end
32
+ return m
33
+ end
34
+
35
+ function robustness ()
36
+ unreg = m -> unregister (m, :original_model_variables )
37
+
38
+ function fresh_model ()
39
+ m = Model ()
40
+ @variable (m, - 10 <= x<= 10 )
41
+ @constraint (m, con1, x<= - 1 )
42
+ @constraint (m, con2, 1 <= x)
43
+ return m, con1, con2
44
+ end
45
+
46
+ # not enough constraints
47
+ m = fresh_model ()[1 ]
48
+ # @test_throws DomainError @disjunction(m, (con1, con2), reformulation=:BMR, name=:y)
49
+ unreg (m)
50
+ # @test_throws DomainError @disjunction(m, con1, reformulation=:BMR, name=:y)
51
+
52
+ # Big-M reformulation without variable bounds defined
53
+ # should only work if user specifies M value
54
+ m = Model ()
55
+ @variable (m, x)
56
+ @constraint (m, con1, x<= - 1 )
57
+ @constraint (m, con2, 1 <= x)
58
+ @test @disjunction (m, con1, con2, reformulation= :BMR , name= :y , M= 11 ) == nothing
59
+ unreg (m)
60
+ @test_throws ErrorException @disjunction (m, con1, con2, reformulation= :BMR , name= :y )
61
+
62
+ # CHR reformulation without variable bounds defined should fail
63
+ m = Model ()
64
+ @variable (m, x)
65
+ @constraint (m, con1, x<= - 1 )
66
+ @constraint (m, con2, 1 <= x)
67
+ @test_throws AssertionError @disjunction (m, con1, con2, reformulation= :CHR , name= :y )
68
+
69
+ # empty constraints on one side of disjunction
70
+ m, con1, con2 = fresh_model ()
71
+ @test @disjunction (m, con1, nothing , reformulation= :BMR , name= :y ) == nothing
72
+ m, con1, con2 = fresh_model ()
73
+ @test @disjunction (m, nothing , con2, reformulation= :CHR , name= :z ) == nothing
74
+ m, con1, con2 = fresh_model ()
75
+ @test @disjunction (m, (con1, con2), nothing , reformulation= :BMR , name= :y ) == nothing
76
+
77
+ end
78
+
79
+ function model_BMR_valid (m)
80
+ # Expecting to see following constraints:
81
+ # x <= -1 + 11 * (1 - y1)
82
+ # -x <= -1 + 11 * (1 - y2)
83
+ # y1 + y2 = 1
84
+ # -10<=x<=10
85
+ cons = (L -> all_constraints (m, L... )). (list_of_constraint_types (m))
86
+ @test sum (length .(cons)) == 5 + 2 # add 2 for y1/y2 binary statements
87
+
88
+ con_strings = replace .(constraints_string (REPLMode, m), " [" => " " ,
89
+ " ]" => " " , " + y" => " +y" , " y" => " * y" ,
90
+ " con1 :" => " " , " con2 :" => " " )
91
+ cons_actual = Meta. parse .(con_strings[1 : 5 ])
92
+ cons_expected = [:(y1 + y2 == 1 ), :(x + 11 * y1 <= 10 ),
93
+ :(- x + 11 * y2 <= 10 ), :(x >= - 10 ), :(x <= 10 )]
94
+ matches = sum (map (i-> isequal (i[1 ], i[2 ]),
95
+ Base. product (cons_expected, cons_actual)))
96
+ @test matches == length (cons_actual)
97
+ end
98
+
99
+ function model_CHR_valid (m)
100
+ # Expecting to see following constraints:
101
+ # x_y1 <= -1 * y1
102
+ # -x_y2 <= -1 * y2
103
+ # y1 + y2 = 1
104
+ # x = x_y1 + x_y2
105
+ # -10 * y1 <= x_y1 <= 10 * y1
106
+ # -10 * y2 <= x_y2 <= 10 * y2
107
+ # -10 <= x <= 10
108
+ # -10 <= x_y1 <= 10
109
+ # -10 <= x_y2 <= 10
110
+ cons = (L -> all_constraints (m, L... )). (list_of_constraint_types (m))
111
+ @test sum (length .(cons)) == 14 + 2 # add 2 for y1/y2 binary statements
112
+
113
+ con_strings = replace .(constraints_string (REPLMode, m), " [" => " " ,
114
+ " ]" => " " , " + y" => " +y" , " y" => " * y" ,
115
+ " con1 :" => " " , " con2 :" => " " )
116
+ println .(con_strings)
117
+ cons_expected = [:(y1 + y2 == 1 ), :(x - x_1 - x_2 == 0 ), :(x >= - 10 ),
118
+ :(x_1 + y1 <= 0 ), :(- x_2 + y2 <= 0 ), :(x <= 10 ),
119
+ :(x_1 >= - 10 ), :(x_1 <= 10 ), :(x_2 >= - 10 ), :(x_2 <= 10 ),
120
+ :(- 10 * y1 - x_1 <= 0 ), :(- 10 * y1 + x_1 <= 0 ),
121
+ :(- 10 * y2 - x_2 <= 0 ), :(- 10 * y2 + x_2 <= 0 )]
122
+ cons_actual = Meta. parse .(con_strings[1 : 14 ])
123
+ matches = sum (map (i-> isequal (i[1 ], i[2 ]),
124
+ Base. product (cons_expected, cons_actual)))
125
+ @test matches == length (cons_actual)
126
+ end
127
+
128
+ robustness ()
129
+
130
+ m1 = simple_example (:BMR )
131
+ model_BMR_valid (m1)
132
+
133
+ m2 = simple_example (:CHR )
134
+ model_CHR_valid (m2)
135
+
136
+ end
0 commit comments