1 #include "lp_writer.hpp"
2 
LPBase(std::shared_ptr<ExpressionBase> _constant_expr,std::vector<std::shared_ptr<ExpressionBase>> & _linear_coefficients,std::vector<std::shared_ptr<Var>> & _linear_vars,std::vector<std::shared_ptr<ExpressionBase>> & _quadratic_coefficients,std::vector<std::shared_ptr<Var>> & _quadratic_vars_1,std::vector<std::shared_ptr<Var>> & _quadratic_vars_2)3 LPBase::LPBase(std::shared_ptr<ExpressionBase> _constant_expr,
4 	       std::vector<std::shared_ptr<ExpressionBase> > &_linear_coefficients,
5 	       std::vector<std::shared_ptr<Var> > &_linear_vars,
6 	       std::vector<std::shared_ptr<ExpressionBase> > &_quadratic_coefficients,
7 	       std::vector<std::shared_ptr<Var> > &_quadratic_vars_1,
8 	       std::vector<std::shared_ptr<Var> > &_quadratic_vars_2)
9 {
10   constant_expr = _constant_expr;
11 
12   linear_coefficients = std::make_shared<std::vector<std::shared_ptr<ExpressionBase> > >();
13   linear_vars = std::make_shared<std::vector<std::shared_ptr<Var> > >();
14   for (unsigned int ndx=0; ndx<_linear_vars.size(); ++ndx)
15     {
16       linear_coefficients->push_back(_linear_coefficients[ndx]);
17       linear_vars->push_back(_linear_vars[ndx]);
18     }
19 
20   quadratic_coefficients = std::make_shared<std::vector<std::shared_ptr<ExpressionBase> > >();
21   quadratic_vars_1 = std::make_shared<std::vector<std::shared_ptr<Var> > >();
22   quadratic_vars_2 = std::make_shared<std::vector<std::shared_ptr<Var> > >();
23   for (unsigned int ndx=0; ndx<_quadratic_coefficients.size(); ++ndx)
24     {
25       quadratic_coefficients->push_back(_quadratic_coefficients[ndx]);
26       quadratic_vars_1->push_back(_quadratic_vars_1[ndx]);
27       quadratic_vars_2->push_back(_quadratic_vars_2[ndx]);
28     }
29 }
30 
31 
add_constraint(std::shared_ptr<LPConstraint> con)32 void LPWriter::add_constraint(std::shared_ptr<LPConstraint> con)
33 {
34   con->index = current_cons_index;
35   ++current_cons_index;
36   constraints->insert(con);
37 }
38 
39 
remove_constraint(std::shared_ptr<LPConstraint> con)40 void LPWriter::remove_constraint(std::shared_ptr<LPConstraint> con)
41 {
42   con->index = -1;
43   constraints->erase(con);
44 }
45 
46 
constraint_sorter(std::shared_ptr<LPConstraint> con1,std::shared_ptr<LPConstraint> con2)47 bool constraint_sorter(std::shared_ptr<LPConstraint> con1, std::shared_ptr<LPConstraint> con2)
48 {
49   return con1->index < con2->index;
50 }
51 
52 
write_expr(std::ofstream & f,std::shared_ptr<LPBase> obj,bool is_objective)53 void write_expr(std::ofstream &f, std::shared_ptr<LPBase> obj, bool is_objective)
54 {
55   double coef;
56   for (unsigned int ndx=0; ndx<obj->linear_coefficients->size(); ++ndx)
57     {
58       coef = obj->linear_coefficients->at(ndx)->evaluate();
59       if (coef >= 0)
60 	{
61 	  f << "+";
62 	}
63       else
64 	{
65 	  f << "-";
66 	}
67       f << std::abs(coef) << " ";
68       f << obj->linear_vars->at(ndx)->name << " \n";
69     }
70   if (is_objective)
71     {
72       f << "+1 obj_const \n";
73     }
74 
75   if (obj->quadratic_coefficients->size() != 0)
76     {
77       f << "+ [ \n";
78       for (unsigned int ndx=0; ndx<obj->quadratic_coefficients->size(); ++ndx)
79 	{
80 	  coef = obj->quadratic_coefficients->at(ndx)->evaluate();
81 	  if (is_objective)
82 	    {
83 	      coef *= 2;
84 	    }
85 	  if (coef >= 0)
86 	    {
87 	      f << "+";
88 	    }
89 	  else
90 	    {
91 	      f << "-";
92 	    }
93 	  f << std::abs(coef) << " ";
94 	  f << obj->quadratic_vars_1->at(ndx)->name << " * ";
95 	  f << obj->quadratic_vars_2->at(ndx)->name << " \n";
96 	}
97       f << "] ";
98       if (is_objective)
99 	{
100 	  f << "/ 2 ";
101 	}
102       f << "\n";
103     }
104 }
105 
106 
write(std::string filename)107 void LPWriter::write(std::string filename)
108 {
109   std::ofstream f;
110   f.open(filename);
111   f.precision(17);
112 
113   if (objective->sense == 0)
114     {
115       f << "minimize\n";
116     }
117   else
118     {
119       f << "maximize\n";
120     }
121 
122   f << objective->name << ": \n";
123   write_expr(f, objective, true);
124 
125   f << "\ns.t.\n\n";
126 
127   std::vector<std::shared_ptr<LPConstraint> > sorted_constraints;
128   for (std::shared_ptr<LPConstraint> con : *constraints)
129     {
130       sorted_constraints.push_back(con);
131     }
132   std::sort(sorted_constraints.begin(), sorted_constraints.end(), constraint_sorter);
133   int sorted_con_index = 0;
134   for (std::shared_ptr<LPConstraint> con : sorted_constraints)
135     {
136       con->index = sorted_con_index;
137       sorted_con_index += 1;
138     }
139   current_cons_index = constraints->size();
140 
141   std::vector<std::shared_ptr<LPConstraint> > active_constraints;
142   for (std::shared_ptr<LPConstraint> con : sorted_constraints)
143     {
144       if (con->active)
145 	{
146 	  active_constraints.push_back(con);
147 	}
148     }
149 
150   double con_lb;
151   double con_ub;
152   double body_constant_val;
153   for (std::shared_ptr<LPConstraint> con : active_constraints)
154     {
155       con_lb = con->lb->evaluate();
156       con_ub = con->ub->evaluate();
157       body_constant_val = con->constant_expr->evaluate();
158       if (con_lb == con_ub)
159 	{
160 	  con_lb -= body_constant_val;
161 	  con_ub = con_lb;
162 	  f << con->name << "_eq: \n";
163 	  write_expr(f, con, false);
164 	  f << "= " << con_lb << " \n\n";
165 	}
166       else if (con_lb > -inf && con_ub < inf)
167 	{
168 	  con_lb -= body_constant_val;
169 	  con_ub -= body_constant_val;
170 	  f << con->name << "_lb: \n";
171 	  write_expr(f, con, false);
172 	  f << ">= " << con_lb << " \n\n";
173 	  f << con->name << "_ub: \n";
174 	  write_expr(f, con, false);
175 	  f << "<= " << con_ub << " \n\n";
176 	}
177       else if (con_lb > -inf)
178 	{
179 	  con_lb -= body_constant_val;
180 	  f << con->name << "_lb: \n";
181 	  write_expr(f, con, false);
182 	  f << ">= " << con_lb << " \n\n";
183 	}
184       else if (con_ub < inf)
185 	{
186 	  con_ub -= body_constant_val;
187 	  f << con->name << "_ub: \n";
188 	  write_expr(f, con, false);
189 	  f << "<= " << con_ub << " \n\n";
190 	}
191     }
192 
193   f << "obj_const_con_eq: \n";
194   f << "+1 obj_const \n";
195   f << "= " << objective->constant_expr->evaluate() << " \n\n";
196 
197   for (std::shared_ptr<LPConstraint> con : active_constraints)
198     {
199       for (std::shared_ptr<Var> v : *(con->linear_vars))
200 	{
201 	  v->index = -1;
202 	}
203       for (std::shared_ptr<Var> v : *(con->quadratic_vars_1))
204 	{
205 	  v->index = -1;
206 	}
207       for (std::shared_ptr<Var> v : *(con->quadratic_vars_2))
208 	{
209 	  v->index = -1;
210 	}
211     }
212 
213   for (std::shared_ptr<Var> v : *(objective->linear_vars))
214     {
215       v->index = -1;
216     }
217   for (std::shared_ptr<Var> v : *(objective->quadratic_vars_1))
218     {
219       v->index = -1;
220     }
221   for (std::shared_ptr<Var> v : *(objective->quadratic_vars_2))
222     {
223       v->index = -1;
224     }
225 
226   std::vector<std::shared_ptr<Var> > active_vars;
227   for (std::shared_ptr<LPConstraint> con : active_constraints)
228     {
229       for (std::shared_ptr<Var> v : *(con->linear_vars))
230 	{
231 	  if (v->index == -1)
232 	    {
233 	      v->index = -2;
234 	      active_vars.push_back(v);
235 	    }
236 	}
237       for (std::shared_ptr<Var> v : *(con->quadratic_vars_1))
238 	{
239 	  if (v->index == -1)
240 	    {
241 	      v->index = -2;
242 	      active_vars.push_back(v);
243 	    }
244 	}
245       for (std::shared_ptr<Var> v : *(con->quadratic_vars_2))
246 	{
247 	  if (v->index == -1)
248 	    {
249 	      v->index = -2;
250 	      active_vars.push_back(v);
251 	    }
252 	}
253     }
254 
255   for (std::shared_ptr<Var> v : *(objective->linear_vars))
256     {
257       if (v->index == -1)
258 	{
259 	  v->index = -2;
260 	  active_vars.push_back(v);
261 	}
262     }
263   for (std::shared_ptr<Var> v : *(objective->quadratic_vars_1))
264     {
265       if (v->index == -1)
266 	{
267 	  v->index = -2;
268 	  active_vars.push_back(v);
269 	}
270     }
271   for (std::shared_ptr<Var> v : *(objective->quadratic_vars_2))
272     {
273       if (v->index == -1)
274 	{
275 	  v->index = -2;
276 	  active_vars.push_back(v);
277 	}
278     }
279 
280   f << "Bounds\n";
281   std::vector<std::shared_ptr<Var> > binaries;
282   std::vector<std::shared_ptr<Var> > integers;
283   for (std::shared_ptr<Var> v : active_vars)
284     {
285       if (v->domain == "binary")
286 	{
287 	  binaries.push_back(v);
288 	}
289       else if (v->domain == "integer")
290 	{
291 	  integers.push_back(v);
292 	}
293       if (v->fixed)
294 	{
295 	  f << "  " << v->value << " <= " << v->name << " <= " << v->value << " \n";
296 	}
297       else
298 	{
299 	  f << "  ";
300 	  if (v->lb <= -inf)
301 	    {
302 	      f << "-inf";
303 	    }
304 	  else
305 	    {
306 	      f << v->lb;
307 	    }
308 	  f << " <= " << v->name << " <= ";
309 	  if (v->ub >= inf)
310 	    {
311 	      f << "+inf";
312 	    }
313 	  else
314 	    {
315 	      f << v->ub;
316 	    }
317 	  f << " \n";
318 	}
319     }
320   f << "-inf <= obj_const <= +inf\n\n";
321 
322   if (binaries.size() > 0)
323     {
324       f << "Binaries \n";
325       for (std::shared_ptr<Var> v : binaries)
326 	{
327 	  f << v->name << " \n";
328 	}
329     }
330 
331   if (integers.size() > 0)
332     {
333       f << "Generals \n";
334       for (std::shared_ptr<Var> v : integers)
335 	{
336 	  f << v->name << " \n";
337 	}
338     }
339 
340   f << "end\n";
341 
342   f.close();
343 
344   solve_cons = active_constraints;
345   solve_vars = active_vars;
346 }
347 
348 
get_solve_vars()349 std::vector<std::shared_ptr<Var> > LPWriter::get_solve_vars()
350 {
351   return solve_vars;
352 }
353 
354 
get_solve_cons()355 std::vector<std::shared_ptr<LPConstraint> > LPWriter::get_solve_cons()
356 {
357   return solve_cons;
358 }
359 
360 
process_lp_constraints(py::list cons,py::object writer)361 void process_lp_constraints(py::list cons, py::object writer)
362 {
363   py::object generate_standard_repn = py::module_::import("pyomo.repn.standard_repn").attr("generate_standard_repn");
364   py::object id = py::module_::import("pyomo.contrib.appsi.writers.lp_writer").attr("id");
365   py::object ScalarParam = py::module_::import("pyomo.core.base.param").attr("ScalarParam");
366   py::object _ParamData = py::module_::import("pyomo.core.base.param").attr("_ParamData");
367   py::object NumericConstant = py::module_::import("pyomo.core.expr.numvalue").attr("NumericConstant");
368   py::str cname;
369   py::object repn;
370   py::object getSymbol = writer.attr("_symbol_map").attr("getSymbol");
371   py::object labeler = writer.attr("_con_labeler");
372   py::object dfs_postorder_stack = writer.attr("_walker").attr("dfs_postorder_stack");
373   LPWriter* c_writer = writer.attr("_writer").cast<LPWriter*>();
374   py::dict var_map = writer.attr("_pyomo_var_to_solver_var_map");
375   py::dict param_map = writer.attr("_pyomo_param_to_solver_param_map");
376   py::dict pyomo_con_to_solver_con_map = writer.attr("_pyomo_con_to_solver_con_map");
377   py::dict solver_con_to_pyomo_con_map = writer.attr("_solver_con_to_pyomo_con_map");
378   py::int_ ione = 1;
379   py::float_ fone = 1.0;
380   py::type int_ = py::type::of(ione);
381   py::type float_ = py::type::of(fone);
382   py::type tmp_type = py::type::of(ione);
383   std::shared_ptr<ExpressionBase> _const;
384   py::object repn_constant;
385   std::vector<std::shared_ptr<ExpressionBase> > lin_coef;
386   std::vector<std::shared_ptr<Var> > lin_vars;
387   py::list repn_linear_coefs;
388   py::list repn_linear_vars;
389   std::vector<std::shared_ptr<ExpressionBase> > quad_coef;
390   std::vector<std::shared_ptr<Var> > quad_vars_1;
391   std::vector<std::shared_ptr<Var> > quad_vars_2;
392   py::list repn_quad_coefs;
393   py::list repn_quad_vars;
394   std::shared_ptr<LPConstraint> lp_con;
395   py::tuple v_tuple;
396   py::handle lb;
397   py::handle ub;
398   py::tuple lower_body_upper;
399   py::dict active_constraints = writer.attr("_active_constraints");
400   py::object nonlinear_expr;
401   for (py::handle c : cons)
402     {
403       lower_body_upper = active_constraints[c];
404       cname = getSymbol(c, labeler);
405       repn = generate_standard_repn(lower_body_upper[1], "compute_values"_a=false, "quadratic"_a=true);
406       nonlinear_expr = repn.attr("nonlinear_expr");
407       if (!(nonlinear_expr.is(py::none())))
408         {
409           throw py::value_error("cannot write an LP file with a nonlinear constraint");
410         }
411       repn_constant = repn.attr("constant");
412       tmp_type = py::type::of(repn_constant);
413       if (tmp_type.is(int_) || tmp_type.is(float_))
414         {
415           _const = std::make_shared<Constant>(repn_constant.cast<double>());
416         }
417       else if(tmp_type.is(ScalarParam) || tmp_type.is(_ParamData))
418         {
419           _const = param_map[id(repn_constant)].cast<std::shared_ptr<ExpressionBase> >();
420         }
421       else
422         {
423           _const = dfs_postorder_stack(repn_constant).cast<std::shared_ptr<ExpressionBase> >();
424         }
425       lin_coef.clear();
426       repn_linear_coefs = repn.attr("linear_coefs");
427       for (py::handle coef : repn_linear_coefs)
428         {
429           tmp_type = py::type::of(coef);
430           if (tmp_type.is(int_) || tmp_type.is(float_))
431             {
432               lin_coef.push_back(std::make_shared<Constant>(coef.cast<double>()));
433             }
434           else if(tmp_type.is(ScalarParam) || tmp_type.is(_ParamData))
435             {
436               lin_coef.push_back(param_map[id(coef)].cast<std::shared_ptr<ExpressionBase> >());
437             }
438           else
439             {
440               lin_coef.push_back(dfs_postorder_stack(coef).cast<std::shared_ptr<ExpressionBase> >());
441             }
442         }
443       lin_vars.clear();
444       repn_linear_vars = repn.attr("linear_vars");
445       for (py::handle v : repn_linear_vars)
446         {
447           lin_vars.push_back(var_map[id(v)].cast<std::shared_ptr<Var> >());
448         }
449       quad_coef.clear();
450       repn_quad_coefs = repn.attr("quadratic_coefs");
451       for (py::handle coef : repn_quad_coefs)
452         {
453           tmp_type = py::type::of(coef);
454           if (tmp_type.is(int_) || tmp_type.is(float_))
455             {
456               quad_coef.push_back(std::make_shared<Constant>(coef.cast<double>()));
457             }
458           else if(tmp_type.is(ScalarParam) || tmp_type.is(_ParamData))
459             {
460               quad_coef.push_back(param_map[id(coef)].cast<std::shared_ptr<ExpressionBase> >());
461             }
462           else
463             {
464               quad_coef.push_back(dfs_postorder_stack(coef).cast<std::shared_ptr<ExpressionBase> >());
465             }
466         }
467       quad_vars_1.clear();
468       quad_vars_2.clear();
469       repn_quad_vars = repn.attr("quadratic_vars");
470       for (py::handle v_tuple_handle : repn_quad_vars)
471         {
472           v_tuple = v_tuple_handle.cast<py::tuple>();
473           quad_vars_1.push_back(var_map[id(v_tuple[0])].cast<std::shared_ptr<Var> >());
474           quad_vars_2.push_back(var_map[id(v_tuple[1])].cast<std::shared_ptr<Var> >());
475         }
476 
477       lp_con = std::make_shared<LPConstraint>(_const, lin_coef, lin_vars, quad_coef, quad_vars_1, quad_vars_2);
478       lp_con->name = cname;
479 
480       lb = lower_body_upper[0];
481       ub = lower_body_upper[2];
482       if (!lb.is(py::none()))
483         {
484           tmp_type = py::type::of(lb);
485           if (tmp_type.is(NumericConstant))
486             {
487               lp_con->lb = std::make_shared<Constant>(lb.attr("value").cast<double>());
488             }
489           else if (tmp_type.is(int_) || tmp_type.is(float_))
490             {
491               lp_con->lb = std::make_shared<Constant>(lb.cast<double>());
492             }
493           else if(tmp_type.is(ScalarParam) || tmp_type.is(_ParamData))
494             {
495               lp_con->lb = param_map[id(lb)].cast<std::shared_ptr<ExpressionBase> >();
496             }
497           else
498             {
499               lp_con->lb = dfs_postorder_stack(lb).cast<std::shared_ptr<ExpressionBase> >();
500             }
501         }
502       if (!ub.is(py::none()))
503         {
504           tmp_type = py::type::of(ub);
505           if (tmp_type.is(NumericConstant))
506             {
507               lp_con->ub = std::make_shared<Constant>(ub.attr("value").cast<double>());
508             }
509           else if (tmp_type.is(int_) || tmp_type.is(float_))
510             {
511               lp_con->ub = std::make_shared<Constant>(ub.cast<double>());
512             }
513           else if(tmp_type.is(ScalarParam) || tmp_type.is(_ParamData))
514             {
515               lp_con->ub = param_map[id(ub)].cast<std::shared_ptr<ExpressionBase> >();
516             }
517           else
518             {
519               lp_con->ub = dfs_postorder_stack(ub).cast<std::shared_ptr<ExpressionBase> >();
520             }
521         }
522       c_writer->add_constraint(lp_con);
523       pyomo_con_to_solver_con_map[c] = py::cast(lp_con);
524       solver_con_to_pyomo_con_map[py::cast(lp_con)] = c;
525     }
526 }
527