Skip to content

Commit e089ad4

Browse files
committed
Capture solver output in Python
1 parent f75687b commit e089ad4

File tree

12 files changed

+108
-23
lines changed

12 files changed

+108
-23
lines changed

include/pyoptinterface/copt_model.hpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ extern "C"
9898
B(COPT_GetColUpperIIS); \
9999
B(COPT_GetRowLowerIIS); \
100100
B(COPT_GetRowUpperIIS); \
101-
B(COPT_GetSOSIIS);
101+
B(COPT_GetSOSIIS); \
102+
B(COPT_SetLogCallback);
102103

103104
namespace copt
104105
{
@@ -170,6 +171,13 @@ struct COPTCallbackUserdata
170171
bool cb_requires_submit_solution = false;
171172
};
172173

174+
using COPTLoggingCallback = std::function<void(const char *)>;
175+
176+
struct COPTLoggingCallbackUserdata
177+
{
178+
COPTLoggingCallback callback;
179+
};
180+
173181
class COPTModel : public OnesideLinearConstraintMixin<COPTModel>,
174182
public TwosideLinearConstraintMixin<COPTModel>,
175183
public OnesideQuadraticConstraintMixin<COPTModel>,
@@ -304,6 +312,11 @@ class COPTModel : public OnesideLinearConstraintMixin<COPTModel>,
304312
int _constraint_index(const ConstraintIndex &constraint);
305313
int _checked_constraint_index(const ConstraintIndex &constraint);
306314

315+
// Control logging
316+
void set_logging(const COPTLoggingCallback &callback);
317+
318+
COPTLoggingCallbackUserdata m_logging_callback_userdata;
319+
307320
// Callback
308321
void set_callback(const COPTCallback &callback, int cbctx);
309322

include/pyoptinterface/gurobi_model.hpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
B(GRBloadenv); \
7474
B(GRBfreeenv); \
7575
B(GRBstartenv); \
76+
B(GRBsetlogcallbackfunc); \
7677
B(GRBconverttofixed); \
7778
B(GRBcomputeIIS);
7879

@@ -104,10 +105,7 @@ class GurobiEnv
104105

105106
void check_error(int error);
106107

107-
private:
108108
GRBenv *m_env = nullptr;
109-
110-
friend class GurobiModel;
111109
};
112110

113111
struct GRBfreemodelT
@@ -138,6 +136,13 @@ struct GurobiCallbackUserdata
138136
bool cb_requires_submit_solution = false;
139137
};
140138

139+
using GurobiLoggingCallback = std::function<void(const char *)>;
140+
141+
struct GurobiLoggingCallbackUserdata
142+
{
143+
GurobiLoggingCallback callback;
144+
};
145+
141146
class GurobiModel : public OnesideLinearConstraintMixin<GurobiModel>,
142147
public OnesideQuadraticConstraintMixin<GurobiModel>,
143148
public TwosideNLConstraintMixin<GurobiModel>,
@@ -293,6 +298,11 @@ class GurobiModel : public OnesideLinearConstraintMixin<GurobiModel>,
293298
// Non-exported functions
294299
void check_error(int error);
295300

301+
// Control logging
302+
void set_logging(const GurobiLoggingCallback &callback);
303+
304+
GurobiLoggingCallbackUserdata m_logging_callback_userdata;
305+
296306
// Callback
297307
void set_callback(const GurobiCallback &callback);
298308

include/pyoptinterface/mosek_model.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ struct MOSEKfreemodelT
121121
};
122122
};
123123

124+
using MOSEKLoggingCallback = std::function<void(const char *)>;
125+
126+
struct MOSEKLoggingCallbackUserdata
127+
{
128+
MOSEKLoggingCallback callback;
129+
};
130+
124131
class MOSEKModel : public OnesideLinearConstraintMixin<MOSEKModel>,
125132
public TwosideLinearConstraintMixin<MOSEKModel>,
126133
public OnesideQuadraticConstraintMixin<MOSEKModel>,
@@ -206,7 +213,6 @@ class MOSEKModel : public OnesideLinearConstraintMixin<MOSEKModel>,
206213
double getprimalobj();
207214
double getdualobj();
208215

209-
void enable_log();
210216
void disable_log();
211217

212218
// Accessing information of problem
@@ -246,6 +252,11 @@ class MOSEKModel : public OnesideLinearConstraintMixin<MOSEKModel>,
246252
MSKint32t _constraint_index(const ConstraintIndex &constraint);
247253
MSKint32t _checked_constraint_index(const ConstraintIndex &constraint);
248254

255+
// Control logging
256+
void set_logging(const MOSEKLoggingCallback &callback);
257+
258+
MOSEKLoggingCallbackUserdata m_logging_callback_userdata;
259+
249260
private:
250261
MonotoneIndexer<MSKint32t> m_variable_index;
251262

lib/copt_model.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1390,8 +1390,24 @@ void COPTEnvConfig::set(const char *param_name, const char *value)
13901390
check_error(error);
13911391
}
13921392

1393+
// Logging callback
1394+
static void RealLoggingCallbackFunction(char *msg, void *logdata)
1395+
{
1396+
auto real_logdata = static_cast<COPTLoggingCallbackUserdata *>(logdata);
1397+
auto &callback = real_logdata->callback;
1398+
callback(msg);
1399+
}
1400+
1401+
void COPTModel::set_logging(const COPTLoggingCallback &callback)
1402+
{
1403+
m_logging_callback_userdata.callback = callback;
1404+
int error = copt::COPT_SetLogCallback(m_model.get(), &RealLoggingCallbackFunction,
1405+
&m_logging_callback_userdata);
1406+
check_error(error);
1407+
}
1408+
13931409
// Callback
1394-
int RealCOPTCallbackFunction(copt_prob *prob, void *cbdata, int cbctx, void *userdata)
1410+
static int RealCOPTCallbackFunction(copt_prob *prob, void *cbdata, int cbctx, void *userdata)
13951411
{
13961412
auto real_userdata = static_cast<COPTCallbackUserdata *>(userdata);
13971413
auto model = static_cast<COPTModel *>(real_userdata->model);

lib/copt_model_ext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ NB_MODULE(copt_model_ext, m)
149149
BIND_F(version_string)
150150
BIND_F(get_raw_model)
151151

152+
BIND_F(set_logging)
153+
152154
BIND_F(set_callback)
153155
BIND_F(cb_get_info_int)
154156
BIND_F(cb_get_info_double)

lib/gurobi_model.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,8 +1404,25 @@ void GurobiEnv::check_error(int error)
14041404
}
14051405
}
14061406

1407+
// Logging callback
1408+
static int RealLoggingCallbackFunction(char *msg, void *logdata)
1409+
{
1410+
auto real_logdata = static_cast<GurobiLoggingCallbackUserdata *>(logdata);
1411+
auto &callback = real_logdata->callback;
1412+
callback(msg);
1413+
return 0;
1414+
}
1415+
1416+
void GurobiModel::set_logging(const GurobiLoggingCallback &callback)
1417+
{
1418+
m_logging_callback_userdata.callback = callback;
1419+
int error = gurobi::GRBsetlogcallbackfunc(m_model.get(), &RealLoggingCallbackFunction,
1420+
&m_logging_callback_userdata);
1421+
check_error(error);
1422+
}
1423+
14071424
// Callback
1408-
int RealGurobiCallbackFunction(GRBmodel *, void *cbdata, int where, void *usrdata)
1425+
static int RealGurobiCallbackFunction(GRBmodel *, void *cbdata, int where, void *usrdata)
14091426
{
14101427
auto real_userdata = static_cast<GurobiCallbackUserdata *>(usrdata);
14111428
auto model = static_cast<GurobiModel *>(real_userdata->model);

lib/gurobi_model_ext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ NB_MODULE(gurobi_model_ext, m)
153153
BIND_F(version_string)
154154
BIND_F(get_raw_model)
155155

156+
BIND_F(set_logging)
157+
156158
BIND_F(set_callback)
157159
BIND_F(cb_get_info_int)
158160
BIND_F(cb_get_info_double)

lib/mosek_model.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -940,17 +940,6 @@ double MOSEKModel::getdualobj()
940940
return retval;
941941
}
942942

943-
static void printstr(void *handle, const char *str)
944-
{
945-
printf("%s", str);
946-
fflush(stdout);
947-
}
948-
void MOSEKModel::enable_log()
949-
{
950-
auto error = mosek::MSK_linkfunctotaskstream(m_model.get(), MSK_STREAM_LOG, NULL, printstr);
951-
check_error(error);
952-
}
953-
954943
void MOSEKModel::disable_log()
955944
{
956945
auto error = mosek::MSK_linkfunctotaskstream(m_model.get(), MSK_STREAM_LOG, NULL, NULL);
@@ -1402,6 +1391,22 @@ MSKint32t MOSEKModel::_checked_constraint_index(const ConstraintIndex &constrain
14021391
return row;
14031392
}
14041393

1394+
// Logging callback
1395+
static void RealLoggingCallbackFunction(void *handle, const char *msg)
1396+
{
1397+
auto real_logdata = static_cast<MOSEKLoggingCallbackUserdata *>(handle);
1398+
auto &callback = real_logdata->callback;
1399+
callback(msg);
1400+
}
1401+
1402+
void MOSEKModel::set_logging(const MOSEKLoggingCallback &callback)
1403+
{
1404+
m_logging_callback_userdata.callback = callback;
1405+
auto error = mosek::MSK_linkfunctotaskstream(
1406+
m_model.get(), MSK_STREAM_LOG, &m_logging_callback_userdata, &RealLoggingCallbackFunction);
1407+
check_error(error);
1408+
}
1409+
14051410
void *MOSEKModel::get_raw_model()
14061411
{
14071412
return m_model.get();

lib/mosek_model_ext.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <nanobind/stl/tuple.h>
33
#include <nanobind/stl/string.h>
44
#include <nanobind/stl/vector.h>
5+
#include <nanobind/stl/function.h>
56

67
#include "pyoptinterface/mosek_model.hpp"
78

@@ -135,7 +136,7 @@ NB_MODULE(mosek_model_ext, m)
135136
BIND_F(getprimalobj)
136137
BIND_F(getdualobj)
137138

138-
BIND_F(enable_log)
139+
BIND_F(set_logging)
139140
BIND_F(disable_log)
140141

141142
BIND_F(set_variable_name)

src/pyoptinterface/_src/copt.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,10 @@ def __init__(self, env=None):
384384
self.variable_start_values: Dict[VariableIndex, float] = dict()
385385
self.nl_start_values: Dict[VariableIndex, float] = dict()
386386

387+
# override logging
388+
self.set_raw_parameter("LogToConsole", 0)
389+
self.set_logging(print)
390+
387391
def add_variables(self, *args, **kwargs):
388392
return make_variable_tupledict(self, *args, **kwargs)
389393

0 commit comments

Comments
 (0)