Skip to content

Commit

Permalink
Merge branch 'latest' into consider-1911
Browse files Browse the repository at this point in the history
  • Loading branch information
jajhall committed Sep 12, 2024
2 parents a251c86 + c943e04 commit 8b635e0
Show file tree
Hide file tree
Showing 33 changed files with 247 additions and 172 deletions.
4 changes: 3 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* *
* This file is part of the HiGHS linear optimization suite *
* *
* Written and engineered 2008-2023 by Julian Hall, Ivet Galabova, *
* Written and engineered 2008-2024 by Julian Hall, Ivet Galabova, *
* Leona Gottwald and Michael Feldmeier *
* *
* Available as open-source under the MIT License *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *=#
Expand Down Expand Up @@ -90,6 +91,7 @@ Documenter.makedocs(
"options/definitions.md"
],
"Parallel" => "parallel.md",
"Solvers" => "solvers.md",
"Terminology" => "terminology.md",
],
)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/executable.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Executable
# [Executable](@id executable)

For convenience, the executable is assumed to be `bin/highs`.

Expand Down
23 changes: 6 additions & 17 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Get started by following [Install HiGHS](@ref).

## Overview

The standalone [Executable](@ref) allows models to be solved from
The standalone [executable](@ref executable) allows models to be solved from
[MPS](https://en.wikipedia.org/wiki/MPS_(format)) or (CPLEX)
[LP](https://web.mit.edu/lpsolve/doc/CPLEX-format.htm) files, with full control
of the HiGHS run-time options, and the solution can be written to files in human
Expand All @@ -54,23 +54,12 @@ calls. These can be studied via the [C++ header file](https://github.com/ERGO-Co

The C interface cannot make use of the C++ structures and enums, and its methods are documented [explicitly](@ref c-api).

## Solution algorithms

For LPs, HiGHS has implementations of both the revised simplex and interior
point methods. MIPs are solved by branch-and-cut, and QPs by active set.

For LP, the novel features of the dual simplex solver are described in
_Parallelizing the dual revised simplex method_, Q. Huangfu and
J. A. J. Hall, Mathematical Programming Computation, 10 (1), 119-142,
2018 [DOI:
10.1007/s12532-017-0130-5](https://link.springer.com/article/10.1007/s12532-017-0130-5). For
the interior point solver, the reference is _Implementation of an
interior point method with basis preconditioning_, Mathematical
Programming Computation, 12, 603-635, 2020. [DOI:
10.1007/s12532-020-00181-8](https://link.springer.com/article/10.1007/s12532-020-00181-8). There
are no specific references to the techniques used in the MIP or QP
solvers.
## Solvers

For LPs, HiGHS has implementations of the revised simplex method,
interior point method, and PDLP first order method. MIPs are solved by
branch-and-cut, and QPs by active set. More information on the HiGHS
solvers is [available](@ref solvers).

## Citing HiGHS

Expand Down
4 changes: 2 additions & 2 deletions docs/src/options/definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- Type: string
- Default: "choose"

## solver
## [solver](@id option-solver)
- Solver option: "simplex", "choose", "ipm" or "pdlp". If "simplex"/"ipm"/"pdlp" is chosen then, for a MIP (QP) the integrality constraint (quadratic term) will be ignored
- Type: string
- Default: "choose"
Expand Down Expand Up @@ -109,7 +109,7 @@
- Range: {-2147483647, 2147483647}
- Default: 0

## simplex\_strategy
## [simplex\_strategy](@id option-simplex_strategy)
- Strategy for simplex solver 0 => Choose; 1 => Dual (serial); 2 => Dual (PAMI); 3 => Dual (SIP); 4 => Primal
- Type: integer
- Range: {0, 4}
Expand Down
4 changes: 2 additions & 2 deletions docs/src/options/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
The options that control HiGHS are of four types: `boolean`, `integer`, `double`
and `string`. Their values can be specified:

* via the command line when running the [Executable](@ref)
* via the command line when running the [Executable](@ref executable)
* via method calls when running HiGHS in an application.

## Options file

When running the [Executable](@ref) via the command line, some options values
When running the [Executable](@ref executable) via the command line, some options values
can be set explicitly in the command, and all options can be set by means of an
options file.

Expand Down
2 changes: 1 addition & 1 deletion docs/src/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ has a variant allowing concurrent processing. This variant is used
when the
[parallel](@ref)
option is set "on", by specifying `--parallel` when running the
[executable](@ref Executable) via
[executable](@ref executable) via
the command line, or by setting it via a library call in an
application.

Expand Down
60 changes: 60 additions & 0 deletions docs/src/solvers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# [Solvers](@id solvers)

## LP

HiGHS has implementations of the three main solution techniques for
LP. HiGHS will choose the most appropriate technique for a given
problem, but this can be over-ridden by setting the option
[__solver__](@ref option-solver).

#### Simplex

HiGHS has efficient implementations of both the primal and dual
simplex methods, although the dual simplex solver is likely to be
faster and is more robust, so is used by default. The novel features
of the dual simplex solver are described in

_Parallelizing the dual revised simplex method_, Q. Huangfu and
J. A. J. Hall, Mathematical Programming Computation, 10 (1), 119-142,
2018 [DOI:
10.1007/s12532-017-0130-5](https://link.springer.com/article/10.1007/s12532-017-0130-5).

* Setting the option [__solver__](@ref option-solver) to "simplex" forces the simplex solver to be used
* The option [__simplex\_strategy__](@ref option-simplex_strategy)
determines whether the primal solver or one of the parallel solvers is
to be used.

#### Interior point

HiGHS has one interior point (IPM) solver based on the preconditioned conjugate gradient method, as discussed in

_Implementation of an interior point method with basis
preconditioning_, Mathematical Programming Computation, 12, 603-635, 2020. [DOI:
10.1007/s12532-020-00181-8](https://link.springer.com/article/10.1007/s12532-020-00181-8).

This solver is serial. An interior point solver based on direct factorization is being developed.

Setting the option [__solver__](@ref option-solver) to "ipm" forces the IPM solver to be used

#### Primal-dual hybrid gradient method

HiGHS includes the [cuPDLP-C](https://github.com/COPT-Public/cuPDLP-C)
primal-dual hybrid gradient method for LP (PDLP). Currently this only
runs on CPU, so it is unlikely to be competitive with the HiGHS
interior point or simplex solvers. Enabling HiGHS to run PDLP on a GPU
is work in progress.

Setting the option [__solver__](@ref option-solver) to "pdlp" forces the PDLP solver to be used

## MIP

The HiGHS MIP solver uses established branch-and-cut techniques

## QP

The HiGHS solver for convex QP problems uses an established primal
active set method. The new interior point solver will also be able to
solve convex QP problems.



11 changes: 10 additions & 1 deletion src/highs_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,15 @@ std::tuple<HighsStatus, int> highs_getRowByName(Highs* h,
}


HighsStatus highs_run(Highs* h)
{
py::gil_scoped_release release;
HighsStatus status = h->run();
py::gil_scoped_acquire();
return status;
}


PYBIND11_MODULE(_core, m) {
// enum classes
py::enum_<ObjSense>(m, "ObjSense")
Expand Down Expand Up @@ -874,7 +883,7 @@ PYBIND11_MODULE(_core, m) {
.def("writeBasis", &Highs::writeBasis)
.def("postsolve", &highs_postsolve)
.def("postsolve", &highs_mipPostsolve)
.def("run", &Highs::run)
.def("run", &highs_run)
.def("feasibilityRelaxation",
[](Highs& self, double global_lower_penalty, double global_upper_penalty, double global_rhs_penalty,
py::object local_lower_penalty, py::object local_upper_penalty, py::object local_rhs_penalty) {
Expand Down
25 changes: 21 additions & 4 deletions src/highspy/highs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@
from itertools import groupby, product
from operator import itemgetter
from decimal import Decimal
from threading import Thread


class _ThreadingResult:
def __init__(self):
self.out = None


class Highs(_Highs):
"""HiGHS solver interface"""
Expand All @@ -47,6 +54,12 @@ def __init__(self):
def silent(self):
"""Disables solver output to the console."""
super().setOptionValue("output_flag", False)

def _run(self, res):
res.out = super().run()

def run(self):
return self.solve()

# solve
def solve(self):
Expand All @@ -55,11 +68,15 @@ def solve(self):
Returns:
A HighsStatus object containing the solve status.
"""
return super().run()
res = _ThreadingResult()
t = Thread(target=self._run, args=(res,))
t.start()
t.join()
return res.out

def optimize(self):
"""Alias for the solve method."""
return super().run()
return self.solve()

# reset the objective and sense, then solve
def minimize(self, obj=None):
Expand Down Expand Up @@ -91,7 +108,7 @@ def minimize(self, obj=None):
super().changeObjectiveOffset(obj.constant)

super().changeObjectiveSense(ObjSense.kMinimize)
return super().run()
return self.solve()

# reset the objective and sense, then solve
def maximize(self, obj=None):
Expand Down Expand Up @@ -123,7 +140,7 @@ def maximize(self, obj=None):
super().changeObjectiveOffset(obj.constant)

super().changeObjectiveSense(ObjSense.kMaximize)
return super().run()
return self.solve()

def internal_get_value(self, var_index_collection, col_value):
"""Internal method to get the value of a variable in the solution. Could be value or dual."""
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/highs_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// Highs_mipCall or Highs_qpCall, and these methods return the
// appropriate solution information
//
// For sophisticated applications, where esoteric solutiuon
// For sophisticated applications, where esoteric solution
// information is needed, or if a sequence of modified models need to
// be solved, use the Highs_create method to generate a pointer to an
// instance of the C++ Highs class, and then use any of a large number
Expand Down
8 changes: 4 additions & 4 deletions src/io/FilereaderLp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,16 @@ FilereaderRetcode FilereaderLp::readModelFromFile(const HighsOptions& options,
void FilereaderLp::writeToFile(FILE* file, const char* format, ...) {
va_list argptr;
va_start(argptr, format);
char stringbuffer[LP_MAX_LINE_LENGTH + 1] = {};
std::array<char, LP_MAX_LINE_LENGTH + 1> stringbuffer = {};
HighsInt tokenlength =
vsnprintf(stringbuffer, sizeof stringbuffer, format, argptr);
vsnprintf(stringbuffer.data(), stringbuffer.size(), format, argptr);
va_end(argptr);
if (this->linelength + tokenlength >= LP_MAX_LINE_LENGTH) {
fprintf(file, "\n");
fprintf(file, "%s", stringbuffer);
fprintf(file, "%s", stringbuffer.data());
this->linelength = tokenlength;
} else {
fprintf(file, "%s", stringbuffer);
fprintf(file, "%s", stringbuffer.data());
this->linelength += tokenlength;
}
}
Expand Down
32 changes: 19 additions & 13 deletions src/io/HMPSIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,21 @@ FilereaderRetcode readMps(
highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Opened file OK\n");
// Input buffer
const HighsInt lmax = 128;
char line[lmax];
char flag[2] = {0, 0};
double data[3];
std::array<char, lmax> line;
std::array<char, 2> flag = {0, 0};
std::array<double, 3> data;

HighsInt num_alien_entries = 0;
HighsVarType integerCol = HighsVarType::kContinuous;

// Load NAME
load_mpsLine(file, integerCol, lmax, line, flag, data);
load_mpsLine(file, integerCol, lmax, line.data(), flag.data(), data.data());
highsLogDev(log_options, HighsLogType::kInfo, "readMPS: Read NAME OK\n");
// Load OBJSENSE or ROWS
load_mpsLine(file, integerCol, lmax, line, flag, data);
load_mpsLine(file, integerCol, lmax, line.data(), flag.data(), data.data());
if (flag[0] == 'O') {
// Found OBJSENSE
load_mpsLine(file, integerCol, lmax, line, flag, data);
load_mpsLine(file, integerCol, lmax, line.data(), flag.data(), data.data());
std::string sense(&line[2], &line[2] + 3);
// the sense must be "MAX" or "MIN"
if (sense.compare("MAX") == 0) {
Expand All @@ -105,15 +105,16 @@ FilereaderRetcode readMps(
highsLogDev(log_options, HighsLogType::kInfo,
"readMPS: Read OBJSENSE OK\n");
// Load ROWS
load_mpsLine(file, integerCol, lmax, line, flag, data);
load_mpsLine(file, integerCol, lmax, line.data(), flag.data(), data.data());
}

row_names.clear();
col_names.clear();
vector<char> rowType;
map<double, int> rowIndex;
double objName = 0;
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
if (flag[0] == 'N' &&
(objName == 0 || keep_n_rows == kKeepNRowsDeleteRows)) {
// N-row: take the first as the objective and possibly ignore any others
Expand Down Expand Up @@ -149,7 +150,8 @@ FilereaderRetcode readMps(
// line - field 5 non-empty. save_flag1 is used to deduce whether
// the row name and value are from fields 5 and 6, or 3 and 4
HighsInt save_flag1 = 0;
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
HighsInt iRow = rowIndex[data[2]] - 1;
std::string name = "";
if (iRow >= 0) name = row_names[iRow];
Expand Down Expand Up @@ -218,7 +220,8 @@ FilereaderRetcode readMps(
num_alien_entries = 0;
vector<double> RHS(numRow, 0);
save_flag1 = 0;
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
if (data[2] != objName) {
HighsInt iRow = rowIndex[data[2]] - 1;
if (iRow >= 0) {
Expand Down Expand Up @@ -268,7 +271,8 @@ FilereaderRetcode readMps(
rowUpper.resize(numRow);
if (flag[0] == 'R') {
save_flag1 = 0;
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
HighsInt iRow = rowIndex[data[2]] - 1;
if (iRow >= 0) {
if (rowType[iRow] == 'L' || (rowType[iRow] == 'E' && data[0] < 0)) {
Expand Down Expand Up @@ -338,7 +342,8 @@ FilereaderRetcode readMps(
colLower.assign(numCol, 0);
colUpper.assign(numCol, kHighsInf);
if (flag[0] == 'B') {
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
// Find the column index associated with the name "data[2]". If
// the name is in colIndex then the value stored is the true
// column index plus one. Otherwise 0 will be returned.
Expand Down Expand Up @@ -389,7 +394,8 @@ FilereaderRetcode readMps(
HighsInt previous_col = -1;
bool has_diagonal = false;
Qstart.clear();
while (load_mpsLine(file, integerCol, lmax, line, flag, data)) {
while (load_mpsLine(file, integerCol, lmax, line.data(), flag.data(),
data.data())) {
HighsInt iCol0 = colIndex[data[1]] - 1;
std::string name0 = "";
if (iCol0 >= 0) name0 = col_names[iCol0];
Expand Down
Loading

0 comments on commit 8b635e0

Please sign in to comment.