highs_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include "highs_interface.hpp"
26 #include "casadi/core/nlp_tools.hpp"
27 
28 #include <highs_runtime_str.h>
29 namespace casadi {
30 
31  extern "C"
32  int CASADI_CONIC_HIGHS_EXPORT
33  casadi_register_conic_highs(Conic::Plugin* plugin) {
34  plugin->creator = HighsInterface::creator;
35  plugin->name = "highs";
36  plugin->doc = HighsInterface::meta_doc.c_str();
37  plugin->version = CASADI_VERSION;
38  plugin->options = &HighsInterface::options_;
39  plugin->deserialize = &HighsInterface::deserialize;
40  return 0;
41  }
42 
43  extern "C"
44  void CASADI_CONIC_HIGHS_EXPORT casadi_load_conic_highs() {
46  }
47 
48 
49  HighsInterface::HighsInterface(const std::string& name,
50  const std::map<std::string, Sparsity>& st)
51  : Conic(name, st) {
52  }
53 
55  = {{&Conic::options_},
56  {{"highs",
57  {OT_DICT,
58  "Options to be passed to HiGHS."
59  }},
60  }
61  };
62 
63  void HighsInterface::init(const Dict& opts) {
64  // Call the init method of the base class
65  Conic::init(opts);
66 
67  // Read user options
68  for (auto&& op : opts) {
69  if (op.first=="highs") {
70  opts_ = op.second;
71  }
72  }
73 
74  // Initialize read-only members of class that don't require saving
75  // since they can be derived from other read-only members
78 
79  // Allocate memory
80  casadi_int sz_arg, sz_res, sz_w, sz_iw;
81  casadi_highs_work(&p_, &sz_arg, &sz_res, &sz_iw, &sz_w);
82 
83  alloc_arg(sz_arg, true);
84  alloc_res(sz_res, true);
85  alloc_iw(sz_iw, true);
86  alloc_w(sz_w, true);
87  }
88 
90  colinda_.resize(A_.size2()+1);
91  rowa_.resize(A_.nnz());
92  colindh_.resize(H_.size2()+1);
93  rowh_.resize(H_.nnz());
94  copy_vector(A_.colind(), colinda_);
95  copy_vector(A_.row(), rowa_);
96  copy_vector(H_.colind(), colindh_);
97  copy_vector(H_.row(), rowh_);
98  if (!discrete_.empty()) {
99  integrality_.resize(nx_);
100  copy_vector(discrete_, integrality_);
101  }
102  }
103 
104 
105  void codegen_local(CodeGenerator& g, const std::string& name, const std::vector<int>& v) {
106  std::string n = name + "[]";
107  g.local(n, "static const int");
108  std::stringstream init;
109  init << "{";
110  for (casadi_int i=0;i<v.size();++i) {
111  init << v[i];
112  if (i<v.size()-1) init << ", ";
113  }
114  // ISO C forbids empty initializer braces
115  if (v.empty()) init << "0";
116  init << "}";
117  g.init_local(n, init.str());
118  }
119 
121  g << "p.qp = &p_qp;\n";
122  codegen_local(g, "colinda", colinda_);
123  codegen_local(g, "rowa", rowa_);
124  codegen_local(g, "colindh", colindh_);
125  codegen_local(g, "rowh", rowh_);
126  if (!discrete_.empty()) {
127  codegen_local(g, "integrality", integrality_);
128  }
129  g << "p.colinda = colinda;\n";
130  g << "p.rowa = rowa;\n";
131  g << "p.colindh = colindh;\n";
132  g << "p.rowh = rowh;\n";
133  if (discrete_.empty()) {
134  g << "p.integrality = 0;\n";
135  } else {
136  g << "p.integrality = integrality;\n";
137  }
138  g << "casadi_highs_setup(&p);\n";
139  }
140 
142  g << "highs_init_mem(&" + codegen_mem(g) + ");\n";
143  g << "return 0;\n";
144  }
145 
147  g << "highs_free_mem(&" + codegen_mem(g) + ");\n";
148  }
149 
151  p_.qp = &p_qp_;
152  p_.colinda = get_ptr(colinda_);
153  p_.rowa = get_ptr(rowa_);
154  p_.colindh = get_ptr(colindh_);
155  p_.rowh = get_ptr(rowh_);
156  p_.integrality = get_ptr(integrality_);
157 
158  casadi_highs_setup(&p_);
159  }
160 
161  int HighsInterface::init_mem(void* mem) const {
162  if (Conic::init_mem(mem)) return 1;
163  if (!mem) return 1;
164  auto m = static_cast<HighsMemory*>(mem);
165  highs_init_mem(&m->d);
166 
167  m->add_stat("preprocessing");
168  m->add_stat("solver");
169  m->add_stat("postprocessing");
170 
171  return 0;
172  }
173 
174  void HighsInterface::free_mem(void* mem) const {
175  auto m = static_cast<HighsMemory*>(mem);
176  highs_free_mem(&m->d);
177  delete static_cast<HighsMemory*>(mem);
178  }
179 
181  void HighsInterface::set_work(void* mem, const double**& arg, double**& res,
182  casadi_int*& iw, double*& w) const {
183 
184  auto m = static_cast<HighsMemory*>(mem);
185 
186  Conic::set_work(mem, arg, res, iw, w);
187 
188  m->d.prob = &p_;
189  m->d.qp = &m->d_qp;
190 
191  casadi_highs_init(&m->d, &arg, &res, &iw, &w);
192 
193  for (auto&& op : opts_) {
194  HighsInt type;
195  casadi_assert(kHighsStatusOk == Highs_getOptionType(m->d.highs, op.first.c_str(), &type),
196  "Error getting option type for '" + op.first + "'.");
197  HighsInt status;
198  if (type == kHighsOptionTypeBool) {
199  status = Highs_setBoolOptionValue(m->d.highs, op.first.c_str(), op.second.to_bool());
200  } else if (type == kHighsOptionTypeInt) {
201  status = Highs_setIntOptionValue(m->d.highs, op.first.c_str(), op.second.to_int());
202  } else if (type == kHighsOptionTypeDouble) {
203  status = Highs_setDoubleOptionValue(m->d.highs, op.first.c_str(), op.second.to_double());
204  } else if (type == kHighsOptionTypeString) {
205  std::string v = op.second.to_string();
206  status = Highs_setStringOptionValue(m->d.highs, op.first.c_str(), v.c_str());
207  } else {
208  casadi_error("Unknown option type for '" + op.first + "'.");
209  }
210  casadi_assert(kHighsStatusOk == status, "Error setting option '" + op.first + "'.");
211  }
212  }
213 
215  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
216  auto m = static_cast<HighsMemory*>(mem);
217 
218  // Statistics
219  m->fstats.at("solver").tic();
220 
221  casadi_highs_solve(&m->d, arg, res, iw, w);
222  m->fstats.at("solver").toc();
223 
224  return 0;
225  }
226 
228  clear_mem();
229  }
230 
232  qp_codegen_body(g);
244  g.add_include("interfaces/highs_c_api.h");
245 
246  g.auxiliaries << g.sanitize_source(highs_runtime_str, {"casadi_real"});
247 
248 
249  g.local("d", "struct casadi_highs_data*");
250  g.init_local("d", "&" + codegen_mem(g));
251  g.local("p", "struct casadi_highs_prob");
252  set_highs_prob(g);
253 
254  // Setup data structure (corresponds to set_work)
255  g << "d->prob = &p;\n";
256  g << "d->qp = &d_qp;\n";
257  g << "casadi_highs_init(d, &arg, &res, &iw, &w);\n";
258 
259 
260  void* h = Highs_create();
261  for (auto&& op : opts_) {
262  HighsInt type;
263  casadi_assert(kHighsStatusOk == Highs_getOptionType(h, op.first.c_str(), &type),
264  "Error getting option type for '" + op.first + "'.");
265  if (type==kHighsOptionTypeBool) {
266  g << "Highs_setBoolOptionValue(d->highs, " << g.constant(op.first) << ", "
267  << static_cast<int>(op.second.to_bool()) << ");\n";
268  } else if (type==kHighsOptionTypeInt) {
269  g << "Highs_setIntOptionValue(d->highs, " << g.constant(op.first) << ", "
270  << static_cast<int>(op.second.to_int()) << ");\n";
271  } else if (type==kHighsOptionTypeDouble) {
272  g << "Highs_setDoubleOptionValue(d->highs, " << g.constant(op.first) << ", "
273  << op.second.to_double() << ");\n";
274  } else if (type==kHighsOptionTypeString) {
275  g << "Highs_setStringOptionValue(d->highs, " << g.constant(op.first) << ", "
276  << g.constant(op.second.to_string()) << ");\n";
277  } else {
278  casadi_error("Unknown option type for '" + op.first + "'.");
279  }
280  }
281  Highs_destroy(h);
282 
283  g << "casadi_highs_solve(d, arg, res, iw, w);\n";
284 
285  g << "if (!d_qp.success) {\n";
286  if (error_on_fail_) {
287  g << "return -1000;\n";
288  } else {
289  g << "return -1;\n";
290  }
291  g << "}\n";
292  g << "return 0;\n";
293  }
294 
295  Dict HighsInterface::get_stats(void* mem) const {
296  Dict stats = Conic::get_stats(mem);
297  Highs highs;
298  auto m = static_cast<HighsMemory*>(mem);
299  stats["return_status"] =
300  highs.modelStatusToString(static_cast<HighsModelStatus>(m->d.return_status));
301  stats["simplex_iteration_count"] = m->d.simplex_iteration_count;
302  stats["simplex_iteration_count"] = m->d.simplex_iteration_count;
303  stats["ipm_iteration_count"] = m->d.ipm_iteration_count;
304  stats["qp_iteration_count"] = m->d.qp_iteration_count;
305  stats["crossover_iteration_count"] = m->d.crossover_iteration_count;
306  stats["primal_solution_status"] = highs.solutionStatusToString(m->d.primal_solution_status);
307  stats["dual_solution_status"] = highs.solutionStatusToString(m->d.dual_solution_status);
308  stats["basis_validity"] = highs.basisValidityToString(m->d.basis_validity);
309  stats["mip_dual_bound"] = m->d.mip_dual_bound;
310  stats["mip_gap"] = m->d.mip_gap;
311  stats["num_primal_infeasibilities"] = m->d.num_primal_infeasibilities;
312  stats["max_primal_infeasibility"] = m->d.max_primal_infeasibility;
313  stats["sum_primal_infeasibilities"] = m->d.sum_primal_infeasibilities;
314  stats["num_dual_infeasibilities"] = m->d.num_dual_infeasibilities;
315  stats["max_dual_infeasibility"] = m->d.max_dual_infeasibility;
316  stats["sum_dual_infeasibilities"] = m->d.sum_dual_infeasibilities;
317  return stats;
318  }
319 
321  s.version("HighsInterface", 1);
322  s.unpack("HighsInterface::opts", opts_);
323  init_dependent();
324  set_highs_prob();
325  }
326 
329 
330  s.version("HighsInterface", 1);
331  s.pack("HighsInterface::opts", opts_);
332  }
333 
334 } // end namespace casadi
Helper class for C code generation.
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
std::string sanitize_source(const std::string &src, const std::vector< std::string > &inst, bool add_shorthand=true)
Sanitize source files for codegen.
void add_include(const std::string &new_include, bool relative_path=false, const std::string &use_ifdef=std::string())
Add an include file optionally using a relative path "..." instead of an absolute path <....
std::stringstream auxiliaries
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Internal class.
Definition: conic_impl.hpp:44
static const Options options_
Options.
Definition: conic_impl.hpp:83
casadi_int nx_
Number of decision variables.
Definition: conic_impl.hpp:169
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:451
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:166
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:412
std::vector< bool > discrete_
Options.
Definition: conic_impl.hpp:161
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: conic.cpp:738
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: conic.cpp:458
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
casadi_qp_prob< double > p_qp_
Definition: conic_impl.hpp:47
void qp_codegen_body(CodeGenerator &g) const
Generate code for the function body.
Definition: conic.cpp:789
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
std::string codegen_mem(CodeGenerator &g, const std::string &index="mem") const
Get thread-local memory object.
size_t sz_res() const
Get required length of res field.
size_t sz_w() const
Get required length of w field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_arg() const
Get required length of arg field.
size_t sz_iw() const
Get required length of iw field.
static const std::string meta_doc
A documentation string.
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
~HighsInterface() override
Destructor.
HighsInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Constructor using sparsity patterns.
void init(const Dict &opts) override
Initialize.
static const Options options_
Options.
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
Dict get_stats(void *mem) const override
Get all statistics.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
int init_mem(void *mem) const override
Initalize memory block.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void codegen_free_mem(CodeGenerator &g) const override
Codegen for free_mem.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Dict opts_
All HiGHS options.
void codegen_init_mem(CodeGenerator &g) const override
Codegen decref for init_mem.
void free_mem(void *mem) const override
Free memory block.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool error_on_fail_
Throw an exception on failure?
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
The casadi namespace.
Definition: archiver.cpp:28
void copy_vector(const std::vector< S > &s, std::vector< D > &d)
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_CONIC_HIGHS_EXPORT casadi_register_conic_highs(Conic::Plugin *plugin)
void CASADI_CONIC_HIGHS_EXPORT casadi_load_conic_highs()
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
void codegen_local(CodeGenerator &g, const std::string &name, const std::vector< int > &v)
casadi_highs_data< double > d
Options metadata for a class.
Definition: options.hpp:40
std::map< std::string, FStats > fstats
const int * colindh
const int * colinda
const casadi_qp_prob< T1 > * qp
const int * integrality