@@ -36,18 +36,19 @@ def test_min(self):
3636
3737
3838class TestExamplesIPOPT ():
39-
39+ """
40+ Nonlinear test problems taken from the IPOPT documentation and
41+ the Julia documentation: https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/simple_examples/.
42+ """
4043 def test_hs071 (self ):
4144 x = cp .Variable (4 , bounds = [0 ,6 ])
4245 x .value = np .array ([1.0 , 5.0 , 5.0 , 1.0 ])
4346 objective = cp .Minimize (x [0 ]* x [3 ]* (x [0 ] + x [1 ] + x [2 ]) + x [2 ])
44-
45- # Constraints
47+
4648 constraints = [
47- x [0 ]* x [1 ]* x [2 ]* x [3 ] >= 25 , # Product constraint
48- cp .sum_squares (x ) == 40 , # Sum of squares constraint
49+ x [0 ]* x [1 ]* x [2 ]* x [3 ] >= 25 ,
50+ cp .sum_squares (x ) == 40 ,
4951 ]
50- # Create problem
5152 problem = cp .Problem (objective , constraints )
5253 problem .solve (solver = cp .IPOPT , nlp = True )
5354 assert problem .status == cp .OPTIMAL
@@ -63,19 +64,23 @@ def test_portfolio_opt(self):
6364 3.120 , 2.980 , 1.900 , 1.750 , 1.800 ]
6465 })
6566
66- # Compute returns
6767 returns = df .pct_change ().dropna ().values
6868 r = np .mean (returns , axis = 0 )
6969 Q = np .cov (returns .T )
7070
71- # Single-objective optimization
72- x = cp .Variable (3 ) # Non-negative weights
73- x .value = np .array ([10.0 , 10.0 , 10.0 ]) # Initial guess
71+ x = cp .Variable (3 )
72+ x .value = np .array ([10.0 , 10.0 , 10.0 ])
7473 variance = cp .quad_form (x , Q )
7574 expected_return = r @ x
7675
7776 problem = cp .Problem (
78- cp .Minimize (variance ),[cp .sum (x ) <= 1000 , expected_return >= 50 , x >= 0 ])
77+ cp .Minimize (variance ),
78+ [
79+ cp .sum (x ) <= 1000 ,
80+ expected_return >= 50 ,
81+ x >= 0
82+ ]
83+ )
7984 problem .solve (solver = cp .IPOPT , nlp = True )
8085 assert problem .status == cp .OPTIMAL
8186 assert np .allclose (x .value , np .array ([4.97045504e+02 , - 9.89291685e-09 , 5.02954496e+02 ]))
@@ -91,7 +96,6 @@ def test_mle(self):
9196 sigma .value = np .array ([1.0 ])
9297
9398 constraints = [mu == sigma ** 2 , sigma >= 1e-6 ]
94- # Sum of squared residuals
9599 residual_sum = cp .sum_squares (data - mu )
96100 log_likelihood = (
97101 (n / 2 ) * cp .log (1 / (2 * np .pi * (sigma )** 2 ))
@@ -115,28 +119,135 @@ def test_rosenbrock(self):
115119
116120 def test_qcp (self ):
117121 x = cp .Variable (1 )
118- y = cp .Variable (1 , bounds = [0 , np .inf ]) # y >= 0
119- z = cp .Variable (1 , bounds = [0 , np .inf ]) # z >= 0
122+ y = cp .Variable (1 , bounds = [0 , np .inf ])
123+ z = cp .Variable (1 , bounds = [0 , np .inf ])
120124
121125 objective = cp .Maximize (x )
122126
123127 constraints = [
124- x + y + z == 1 , # Linear equality constraint
125- x ** 2 + y ** 2 - z ** 2 <= 0 , # Quadratic constraint: x*x + y*y - z*z <= 0
126- x ** 2 - y * z <= 0 # Quadratic constraint: x*x - y*z <= 0
128+ x + y + z == 1 ,
129+ x ** 2 + y ** 2 - z ** 2 <= 0 ,
130+ x ** 2 - y * z <= 0
127131 ]
128- # Create and solve problem
129132 problem = cp .Problem (objective , constraints )
130133 problem .solve (solver = cp .IPOPT , nlp = True )
131134 assert problem .status == cp .OPTIMAL
132135 assert np .allclose (x .value , np .array ([0.32699284 ]))
133136 assert np .allclose (y .value , np .array ([0.25706586 ]))
134137 assert np .allclose (z .value , np .array ([0.4159413 ]))
135138
139+ def test_acopf (self ):
140+ N = 4
141+
142+ # Conductance/susceptance components
143+ G = np .array (
144+ [
145+ [1.7647 , - 0.5882 , 0.0 , - 1.1765 ],
146+ [- 0.5882 , 1.5611 , - 0.3846 , - 0.5882 ],
147+ [0.0 , - 0.3846 , 1.5611 , - 1.1765 ],
148+ [- 1.1765 , - 0.5882 , - 1.1765 , 2.9412 ],
149+ ]
150+ )
151+
152+ B = np .array (
153+ [
154+ [- 7.0588 , 2.3529 , 0.0 , 4.7059 ],
155+ [2.3529 , - 6.629 , 1.9231 , 2.3529 ],
156+ [0.0 , 1.9231 , - 6.629 , 4.7059 ],
157+ [4.7059 , 2.3529 , 4.7059 , - 11.7647 ],
158+ ]
159+ )
160+
161+ # Assign bounds where fixings are needed
162+ v_lb = np .array ([1.0 , 0.0 , 1.0 , 0.0 ])
163+ v_ub = np .array ([1.0 , 1.5 , 1.0 , 1.5 ])
164+
165+ P_lb = np .array ([- 3.0 , - 0.3 , 0.3 , - 0.2 ])
166+ P_ub = np .array ([3.0 , - 0.3 , 0.3 , - 0.2 ])
167+
168+ Q_lb = np .array ([- 3.0 , - 0.2 , - 3.0 , - 0.15 ])
169+ Q_ub = np .array ([3.0 , - 0.2 , 3.0 , - 0.15 ])
170+
171+ theta_lb = np .array ([0.0 , - np .pi / 2 , - np .pi / 2 , - np .pi / 2 ])
172+ theta_ub = np .array ([0.0 , np .pi / 2 , np .pi / 2 , np .pi / 2 ])
173+
174+ # Create variables with bounds
175+ P = cp .Variable (N , name = "P" ) # Real power for buses
176+ Q = cp .Variable (N , name = "Q" ) # Reactive power for buses
177+ v = cp .Variable (N , name = "v" ) # Voltage magnitude at buses
178+ theta = cp .Variable (N , name = "theta" ) # Voltage angle at buses
179+
180+ # Reshape theta to column vector for broadcasting
181+ theta_col = cp .reshape (theta , (N , 1 ))
182+
183+ # Create constraints list
184+ constraints = []
185+
186+ # Add bound constraints
187+ constraints += [
188+ P >= P_lb ,
189+ P <= P_ub ,
190+ Q >= Q_lb ,
191+ Q <= Q_ub ,
192+ v >= v_lb ,
193+ v <= v_ub ,
194+ theta >= theta_lb ,
195+ theta <= theta_ub
196+ ]
197+ P_balance = cp .multiply (v , (G * cp .cos (theta_col - theta_col .T ) + B * cp .sin (theta_col - theta_col .T )) @ v )
198+ constraints .append (P == P_balance )
199+
200+ # Reactive power balance
201+ Q_balance = cp .multiply (v , (G * cp .sin (theta_col - theta_col .T ) - B * cp .cos (theta_col - theta_col .T )) @ v )
202+ constraints .append (Q == Q_balance )
203+
204+ # Objective: minimize reactive power at buses 1 and 3 (indices 0 and 2)
205+ objective = cp .Minimize (Q [0 ] + Q [2 ])
206+
207+ # Create and solve the problem
208+ problem = cp .Problem (objective , constraints )
209+ problem .solve (solver = cp .IPOPT , nlp = True )
210+ assert problem .status == cp .OPTIMAL
211+
136212class TestNonlinearControl ():
137213
138214 def test_control_of_car (self ):
139215 pass
140216
141- def test_clnl_beam (self ):
142- pass
217+ def test_clnlbeam (self ):
218+ N = 10
219+ h = 1 / N
220+ alpha = 350
221+
222+ t = cp .Variable (N + 1 )
223+ x = cp .Variable (N + 1 )
224+ u = cp .Variable (N + 1 )
225+
226+ objective_terms = []
227+ for i in range (N ):
228+ control_term = 0.5 * h * (u [i + 1 ]** 2 + u [i ]** 2 )
229+ trigonometric_term = 0.5 * alpha * h * (cp .cos (t [i + 1 ]) + cp .cos (t [i ]))
230+ objective_terms .append (control_term + trigonometric_term )
231+
232+ objective = cp .Minimize (cp .sum (objective_terms ))
233+
234+ constraints = [
235+ t >= - 1 ,
236+ t <= 1 ,
237+ x >= - 0.05 ,
238+ x <= 0.05
239+ ]
240+
241+ for i in range (N ):
242+ position_constraint = (x [i + 1 ] - x [i ] -
243+ 0.5 * h * (cp .sin (t [i + 1 ]) + cp .sin (t [i ])) == 0 )
244+ constraints .append (position_constraint )
245+
246+ angle_constraint = (t [i + 1 ] - t [i ] -
247+ 0.5 * h * u [i + 1 ] - 0.5 * h * u [i ] == 0 )
248+ constraints .append (angle_constraint )
249+
250+ problem = cp .Problem (objective , constraints )
251+ problem .solve (solver = cp .IPOPT , nlp = True )
252+ assert problem .status == cp .OPTIMAL
253+ assert problem .value == 3.500e+02
0 commit comments