PANOC-ALM  quadratic-penalty
Nonconvex constrained optimization
casadi_problem.py
Go to the documentation of this file.
1 from typing import Tuple, Union
2 import casadi as cs
3 import panocpy as pa
4 from tempfile import TemporaryDirectory
5 import os
6 
7 
9  f: cs.Function,
10  g: cs.Function,
11  second_order: bool = False,
12  name: str = "PANOC_ALM_problem",
13 ) -> Tuple[cs.CodeGenerator, int, int, int]:
14  """Convert the objective and constraint functions into a CasADi code
15  generator.
16 
17  :param f: Objective function.
18  :param g: Constraint function.
19  :param second_order: Whether to generate functions for evaluating Hessians.
20  :param name: Optional string description of the problem (used for filename).
21 
22  :return: * Code generator that generates the functions and derivatives
23  used by the solvers.
24  * Dimensions of the decision variables (primal dimension).
25  * Number of nonlinear constraints (dual dimension).
26  * Number of parameters.
27  """
28 
29  assert f.n_in() in [1, 2]
30  assert f.n_in() == g.n_in()
31  assert f.size1_in(0) == g.size1_in(0)
32  if f.n_in() == 2:
33  assert f.size1_in(1) == g.size1_in(1)
34  assert f.n_out() == 1
35  assert g.n_out() == 1
36  n = f.size1_in(0)
37  m = g.size1_out(0)
38  p = f.size1_in(1) if f.n_in() == 2 else 0
39  xp = (f.sx_in(0), f.sx_in(1)) if f.n_in() == 2 else (f.sx_in(0),)
40  xp_names = (f.name_in(0), f.name_in(1)) if f.n_in() == 2 else (f.name_in(0),)
41  x = xp[0]
42  y = cs.SX.sym("y", m)
43  v = cs.SX.sym("v", n)
44 
45  L = f(*xp) + cs.dot(y, g(*xp)) if m > 0 else f(*xp)
46 
47  cgname = f"{name}.c"
48  cg = cs.CodeGenerator(cgname)
49  cg.add(
50  cs.Function(
51  "f",
52  [*xp],
53  [f(*xp)],
54  [*xp_names],
55  ["f"],
56  )
57  )
58  cg.add(
59  cs.Function(
60  "grad_f",
61  [*xp],
62  [cs.gradient(f(*xp), x)],
63  [*xp_names],
64  ["grad_f"],
65  )
66  )
67  cg.add(
68  cs.Function(
69  "g",
70  [*xp],
71  [g(*xp)],
72  [*xp_names],
73  ["g"],
74  )
75  )
76  cg.add(
77  cs.Function(
78  "grad_g",
79  [*xp, y],
80  [cs.jtimes(g(*xp), x, y, True)],
81  [*xp_names, "y"],
82  ["grad_g"],
83  )
84  )
85  if second_order:
86  cg.add(
87  cs.Function(
88  "hess_L",
89  [*xp, y],
90  [cs.hessian(L, x)[0]],
91  [*xp_names, "y"],
92  ["hess_L"],
93  )
94  )
95  cg.add(
96  cs.Function(
97  "hess_L_prod",
98  [*xp, y, v],
99  [cs.gradient(cs.jtimes(L, x, v, False), x)],
100  [*xp_names, "y", "v"],
101  ["hess_L_prod"],
102  )
103  )
104  return cg, n, m, p
105 
107  f: cs.Function,
108  g1: cs.Function,
109  g2: cs.Function,
110  second_order: bool = False,
111  name: str = "PANOC_ALM_problem",
112 ) -> Tuple[cs.CodeGenerator, int, int, int]:
113  """Convert the objective and constraint functions into a CasADi code
114  generator.
115 
116  :param f: Objective function.
117  :param g1: ALM constraint function.
118  :param g2: Quadratic penalty constraint function.
119  :param second_order: Whether to generate functions for evaluating Hessians.
120  :param name: Optional string description of the problem (used for filename).
121 
122  :return: * Code generator that generates the functions and derivatives
123  used by the solvers.
124  * Dimensions of the decision variables (primal dimension).
125  * Number of nonlinear constraints (dual dimension).
126  * Number of parameters.
127  """
128 
129  assert f.n_in() in [1, 2]
130  assert f.n_in() == g1.n_in() == g2.n_in()
131  assert f.size1_in(0) == g1.size1_in(0) == g2.size1_in(0)
132  if f.n_in() == 2:
133  assert f.size1_in(1) == g1.size1_in(1) == g2.size1_in(1)
134  assert f.n_out() == 1
135  assert g1.n_out() == 1
136  assert g2.n_out() == 1
137  n = f.size1_in(0)
138  m1 = g1.size1_out(0)
139  m2 = g2.size1_out(0)
140  p = f.size1_in(1) if f.n_in() == 2 else 0
141  xp = (f.sx_in(0), f.sx_in(1)) if f.n_in() == 2 else (f.sx_in(0),)
142  xp_names = (f.name_in(0), f.name_in(1)) if f.n_in() == 2 else (f.name_in(0),)
143  x = xp[0]
144  y1 = cs.SX.sym("y1", m1)
145  y2 = cs.SX.sym("y2", m2)
146  v = cs.SX.sym("v", n)
147 
148  L = f(*xp) + cs.dot(y1, g1(*xp)) if m1 > 0 else f(*xp)
149 
150  cgname = f"{name}.c"
151  cg = cs.CodeGenerator(cgname)
152  cg.add(
153  cs.Function(
154  "f",
155  [*xp],
156  [f(*xp)],
157  [*xp_names],
158  ["f"],
159  )
160  )
161  cg.add(
162  cs.Function(
163  "grad_f",
164  [*xp],
165  [cs.gradient(f(*xp), x)],
166  [*xp_names],
167  ["grad_f"],
168  )
169  )
170  cg.add(
171  cs.Function(
172  "g1",
173  [*xp],
174  [g1(*xp)],
175  [*xp_names],
176  ["g1"],
177  )
178  )
179  cg.add(
180  cs.Function(
181  "grad_g1",
182  [*xp, y1],
183  [cs.jtimes(g1(*xp), x, y1, True)],
184  [*xp_names, "y2"],
185  ["grad_g1"],
186  )
187  )
188  cg.add(
189  cs.Function(
190  "g2",
191  [*xp],
192  [g2(*xp)],
193  [*xp_names],
194  ["g2"],
195  )
196  )
197  cg.add(
198  cs.Function(
199  "grad_g2",
200  [*xp, y2],
201  [cs.jtimes(g2(*xp), x, y2, True) if m2 > 0 else []],
202  [*xp_names, "y2"],
203  ["grad_g2"],
204  )
205  )
206  if second_order:
207  cg.add(
208  cs.Function(
209  "hess_L",
210  [*xp, y1],
211  [cs.hessian(L, x)[0]],
212  [*xp_names, "y1"],
213  ["hess_L"],
214  )
215  )
216  cg.add(
217  cs.Function(
218  "hess_L_prod",
219  [*xp, y1, v],
220  [cs.gradient(cs.jtimes(L, x, v, False), x)],
221  [*xp_names, "y1", "v"],
222  ["hess_L_prod"],
223  )
224  )
225  return cg, n, m1, m2, p
226 
228  cgen: cs.CodeGenerator,
229  n: int,
230  m: int,
231  p: int,
232  name: str = "PANOC_ALM_problem",
233 ) -> Union[pa.Problem, pa.ProblemWithParam]:
234  """Compile the C-code using the given code-generator and load it as a
235  panocpy Problem.
236 
237  :param cgen: Code generator to generate C-code for the costs and the
238  constraints with.
239  :param n: Dimensions of the decision variables (primal dimension).
240  :param m: Number of nonlinear constraints (dual dimension).
241  :param p: Number of parameters.
242  :param name: Optional string description of the problem (used for filename).
243 
244  :return: * Problem specification that can be passed to the solvers.
245  """
246 
247  with TemporaryDirectory(prefix="") as tmpdir:
248  cfile = cgen.generate(os.path.join(tmpdir, ""))
249  sofile = os.path.join(tmpdir, f"{name}.so")
250  os.system(f"cc -fPIC -shared -O3 -march=native {cfile} -o {sofile}")
251  if p > 0:
252  prob = pa.load_casadi_problem_with_param(sofile, n, m)
253  else:
254  prob = pa.load_casadi_problem(sofile, n, m)
255  return prob
256 
258  cgen: cs.CodeGenerator,
259  n: int,
260  m1: int,
261  m2: int,
262  p: int,
263  name: str = "PANOC_full_problem",
265  """Compile the C-code using the given code-generator and load it as a
266  panocpy ProblemFull.
267 
268  :param cgen: Code generator to generate C-code for the costs and the
269  constraints with.
270  :param n: Dimensions of the decision variables (primal dimension).
271  :param m1: Number of ALM constraints (dual dimension 1).
272  :param m2: Number of quadratic penalty constraints (dual dimension 2).
273  :param p: Number of parameters.
274  :param name: Optional string description of the problem (used for filename).
275 
276  :return: * ProblemFull specification that can be passed to the solvers.
277  """
278 
279  with TemporaryDirectory(prefix="") as tmpdir:
280  cfile = cgen.generate(tmpdir)
281  sofile = os.path.join(tmpdir, f"{name}.so")
282  os.system(f"cc -fPIC -shared -O3 -march=native {cfile} -o {sofile}")
283  if p > 0:
284  prob = pa.load_casadi_problem_full_with_param(sofile, n, m1, m2)
285  else:
286  prob = pa.load_casadi_problem_full(sofile, n, m1, m2)
287  return prob
288 
289 
291  f: cs.Function,
292  g: cs.Function,
293  second_order: bool = False,
294  name: str = "PANOC_ALM_problem",
295 ) -> Union[pa.Problem, pa.ProblemWithParam]:
296  """Compile the objective and constraint functions into a panocpy Problem.
297 
298  :param f: Objective function.
299  :param g: Constraint function.
300  :param second_order: Whether to generate functions for evaluating Hessians.
301  :param name: Optional string description of the problem (used for filename).
302 
303  :return: * Problem specification that can be passed to the solvers.
304  """
305  cgen = generate_casadi_problem(f, g, second_order, name)
306  return compile_and_load_problem(*cgen, name)
panocpy.casadi_problem.generate_casadi_problem
Tuple[cs.CodeGenerator, int, int, int] generate_casadi_problem(cs.Function f, cs.Function g, bool second_order=False, str name="PANOC_ALM_problem")
Definition: casadi_problem.py:8
pa::ProblemWithParam
Definition: include/panoc-alm/util/problem.hpp:124
panocpy.test.f
f
Definition: test.py:49
panocpy.casadi_problem.generate_and_compile_casadi_problem
Union[pa.Problem, pa.ProblemWithParam] generate_and_compile_casadi_problem(cs.Function f, cs.Function g, bool second_order=False, str name="PANOC_ALM_problem")
Definition: casadi_problem.py:290
panocpy.casadi_problem.generate_casadi_problem_full
Tuple[cs.CodeGenerator, int, int, int] generate_casadi_problem_full(cs.Function f, cs.Function g1, cs.Function g2, bool second_order=False, str name="PANOC_ALM_problem")
Definition: casadi_problem.py:106
pa::ProblemFullWithParam
Definition: include/panoc-alm/util/problem.hpp:358
panocpy.test.g
g
Definition: test.py:51
pa::ProblemFull
Problem description for minimization problems.
Definition: include/panoc-alm/util/problem.hpp:213
pa::Problem
Problem description for minimization problems.
Definition: include/panoc-alm/util/problem.hpp:24
panocpy.casadi_problem.compile_and_load_problem_full
Union[pa.ProblemFull, pa.ProblemFullWithParam] compile_and_load_problem_full(cs.CodeGenerator cgen, int n, int m1, int m2, int p, str name="PANOC_full_problem")
Definition: casadi_problem.py:257
panocpy.casadi_problem.compile_and_load_problem
Union[pa.Problem, pa.ProblemWithParam] compile_and_load_problem(cs.CodeGenerator cgen, int n, int m, int p, str name="PANOC_ALM_problem")
Definition: casadi_problem.py:227