Skip to content

Commit

Permalink
MAINT: Convert highs_options to submodule
Browse files Browse the repository at this point in the history
  • Loading branch information
HaoZeke committed Oct 14, 2023
1 parent 7dc134b commit 34257fb
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 151 deletions.
60 changes: 14 additions & 46 deletions highspy/highs.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,11 @@
from highspy._highs import simplex_constants as simpc
from highspy._highs import (
# enum classes
ObjSense,
MatrixFormat,
HessianFormat,
SolutionStatus,
BasisValidity,
HighsModelStatus,
HighsPresolveStatus,
HighsBasisStatus,
HighsVarType,
HighsOptionType,
HighsInfoType,
HighsStatus,
HighsLogType,
# classes
Highs_,
HighsSparseMatrix,
HighsLp,
HighsHessian,
HighsModel,
HighsInfo,
HighsOptions,
# structs
HighsSolution,
HighsObjectiveSolution,
HighsBasis,
HighsRangingRecord,
HighsRanging,
# constants
kHighsInf,
kHighsIInf,
)
from highspy import _highs as _h
from itertools import groupby
from operator import itemgetter
from decimal import Decimal


class Highs(Highs_):
class Highs(_h.Highs_):
"""HiGHS solver interface"""
__slots__ = ['_batch', '_vars', '_cons']

Expand All @@ -58,7 +26,7 @@ def minimize(self, obj):
raise Exception('Objective cannot be an inequality')

self.update()
super().changeObjectiveSense(ObjSense.kMinimize)
super().changeObjectiveSense(_h.ObjSense.kMinimize)

# reset objective
super().changeColsCost(self.numVars, range(self.numVars), [0]*self.numVars)
Expand All @@ -80,7 +48,7 @@ def maximize(self, obj):
raise Exception('Objective cannot be an inequality')

self.update()
super().changeObjectiveSense(ObjSense.kMaximize)
super().changeObjectiveSense(_h.ObjSense.kMaximize)

# reset objective
super().changeColsCost(self.numVars, range(self.numVars), [0]*self.numVars)
Expand Down Expand Up @@ -120,16 +88,16 @@ def vals(self, vars):
#
# add variable & useful constants
#
def addVar(self, lb = 0, ub = kHighsInf, obj = 0, type=HighsVarType.kContinuous, name = None):
def addVar(self, lb = 0, ub = _h.kHighsInf, obj = 0, type=_h.HighsVarType.kContinuous, name = None):
var = self._batch.add(obj, lb, ub, type, name, self)
self._vars.append(var)
return var

def addIntegral(self, lb = 0, ub = kHighsInf, obj = 0, name = None):
return self.addVar(lb, ub, obj, HighsVarType.kInteger, name)
def addIntegral(self, lb = 0, ub = _h.kHighsInf, obj = 0, name = None):
return self.addVar(lb, ub, obj, _h.HighsVarType.kInteger, name)

def addBinary(self, obj = 0, name = None):
return self.addVar(0, 1, obj, HighsVarType.kInteger, name)
return self.addVar(0, 1, obj, _h.HighsVarType.kInteger, name)

def removeVar(self, var):
for i in self._vars[var.index+1:]:
Expand All @@ -142,7 +110,7 @@ def getVars(self):

@property
def inf(self):
return kHighsInf
return _h.kHighsInf

@property
def numVars(self):
Expand Down Expand Up @@ -278,8 +246,8 @@ class highs_linear_expression(object):

def __init__(self, other=None):
self.constant = 0
self.LHS = -kHighsInf
self.RHS = kHighsInf
self.LHS = -_h.kHighsInf
self.RHS = _h.kHighsInf

if isinstance(other, highs_linear_expression):
self.vars = list(other.vars)
Expand All @@ -301,7 +269,7 @@ def __neg__(self):
# (LHS <= self <= RHS) <= (other.LHS <= other <= other.RHS)
def __le__(self, other):
if isinstance(other, highs_linear_expression):
if self.LHS != -kHighsInf and self.RHS != kHighsInf and len(other.vars) > 0 or other.LHS != -kHighsInf:
if self.LHS != -_h.kHighsInf and self.RHS != _h.kHighsInf and len(other.vars) > 0 or other.LHS != -_h.kHighsInf:
raise Exception('Cannot construct constraint with variables as bounds.')

# move variables from other to self
Expand All @@ -324,7 +292,7 @@ def __le__(self, other):
# (LHS <= self <= RHS) == (other.LHS <= other <= other.RHS)
def __eq__(self, other):
if isinstance(other, highs_linear_expression):
if self.LHS != -kHighsInf and len(other.vars) > 0 or other.LHS != -kHighsInf:
if self.LHS != -_h.kHighsInf and len(other.vars) > 0 or other.LHS != -_h.kHighsInf:
raise Exception('Cannot construct constraint with variables as bounds.')

# move variables from other to self
Expand All @@ -339,7 +307,7 @@ def __eq__(self, other):
return NotImplemented

elif isinstance(other, (int, float, Decimal)):
if self.LHS != -kHighsInf or self.RHS != kHighsInf:
if self.LHS != -_h.kHighsInf or self.RHS != _h.kHighsInf:
raise Exception('Logic error in constraint equality.')

self.LHS = other
Expand Down
91 changes: 91 additions & 0 deletions highspy/highs_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#include <pybind11/stl.h>

#include <cassert>
#include <map>
#include <mutex>

#include "lp_data/HighsOptions.h"

#include "Highs.h"

Expand Down Expand Up @@ -548,6 +552,55 @@ std::tuple<HighsStatus, int> highs_getRowByName(Highs* h,
return std::make_tuple(status, row);
}

// highs_options.cpp
bool log_to_console = false;
bool output_flag = true;
HighsLogOptions highs_log_options = {nullptr, &output_flag, &log_to_console,
nullptr};

class HighsOptionsManager {
public:
HighsOptionsManager() {
for (const auto& record : highs_options_.records) {
record_type_lookup_.emplace(record->name, record->type);
}
}

const HighsOptions& get_highs_options() const { return highs_options_; }

const std::map<std::string, HighsOptionType>& get_record_type_lookup() const {
return record_type_lookup_;
}

template <typename OptionRecordType, typename T>
bool check_option(const std::string& name, const T value) {
std::lock_guard<std::mutex> guard(highs_options_mutex);
HighsInt idx = 0;
const OptionStatus idx_status = getOptionIndex(
highs_log_options, name.c_str(), highs_options_.records, idx);

if (OptionStatus::kOk != idx_status) {
return false;
}

OptionRecordType& record =
static_cast<OptionRecordType&>(*highs_options_.records.at(idx));
const OptionStatus check_status =
checkOptionValue(highs_log_options, record, value);
if (OptionStatus::kIllegalValue == check_status) {
return false;
}

return true;
}

private:
HighsOptions highs_options_;
std::mutex highs_options_mutex;
std::map<std::string, HighsOptionType> record_type_lookup_;
};


PYBIND11_MODULE(_highs, m) {
// enum classes
py::enum_<ObjSense>(m, "ObjSense")
Expand Down Expand Up @@ -951,6 +1004,44 @@ PYBIND11_MODULE(_highs, m) {
m.attr("kHighsInf") = kHighsInf;
m.attr("kHighsIInf") = kHighsIInf;
// Submodules
// Highs Options
py::module_ highs_options =
m.def_submodule("highs_options", "Submodule for highs options");
py::class_<HighsOptionsManager>(highs_options, "HighsOptionsManager")
.def(py::init<>())
.def("get_option_type",
[](const HighsOptionsManager& manager, const std::string& name) {
const auto& lookup = manager.get_record_type_lookup().find(name);
if (manager.get_record_type_lookup().end() == lookup) {
return -1;
}
return static_cast<int>(lookup->second);
})
.def("get_all_option_types", &HighsOptionsManager::get_record_type_lookup)
.def("get_highs_options_records",
[](const HighsOptionsManager& manager) {
std::vector<std::string> records_names;
for (const auto& record : manager.get_highs_options().records) {
records_names.push_back(record->name);
}
return records_names;
})
.def("check_int_option",
[](HighsOptionsManager& self, const std::string& name, int value) {
return self.check_option<OptionRecordInt, int>(name, value);
})
.def(
"check_double_option",
[](HighsOptionsManager& self, const std::string& name, double value) {
return self.check_option<OptionRecordDouble, double>(name, value);
})
.def("check_string_option", [](HighsOptionsManager& self,
const std::string& name,
const std::string& value) {
return self.check_option<OptionRecordString, std::string>(name, value);
});

// Simplex Constants
py::module_ simplex_constants =
m.def_submodule("simplex_constants", "Submodule for simplex constants");

Expand Down
92 changes: 0 additions & 92 deletions highspy/highs_options.cpp

This file was deleted.

10 changes: 0 additions & 10 deletions highspy/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ py.extension_module(
include_directories: _incdirs,
)

py.extension_module(
'_highs_options',
sources : highsoptions_cpp,
dependencies: [pyb11_dep, highs_dep],
cpp_args: _args,
install: true,
subdir: 'highspy',
include_directories: _incdirs,
)

py.install_sources(
highspy_py,
subdir: 'highspy',
Expand Down
3 changes: 0 additions & 3 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,6 @@ if get_option('with_pybind11')
highspy_cpp = files([
'highspy/highs_bindings.cpp'
])
highsoptions_cpp = files([
'highspy/highs_options.cpp'
])
highspy_py = files([
'highspy/__init__.py',
'highspy/highs.py',
Expand Down

0 comments on commit 34257fb

Please sign in to comment.