WIP: optimize: add HiGHS methods to linprog

Add linprog interface to the HiGHS LP solvers wrapped by @mckib2 in scikit-highs.
Closes gh-11673

@mckib2 I'm going to force-push an update if you don't mind

@mckib2 I'm going to force-push an update if you don't mind

Please do, I haven't pulled anything down yet, I'll have time this evening

OK, I think this is in a state that you can test. So far I've been messing around with test code like:
import numpy as np
from scipy.optimize import linprog
c = [-1, 1]
A_ub = [[1, 0]]
b_ub =[1]
A_eq = [[0, 1]]
b_eq =[2]
bounds = [[1, 1], [2, 2]]
res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method="highs-simplex")

It gets as far as the call to the wrapper with or without any of the contraints (A_ub, b_ub, A_eq, b_eq, bounds), but I've never gotten sensible output. Sometimes it runs, but the solution isn't right, other times with some constraints missing I get KeyErrors or IndexErrors, and I got a segfault when the problem was infeasible. Can you play with this code and take a look at the inputs sent to highs_wrapper - do you agree that they are what you would expect given the input to linprog? How about when there are no constraints (just bounds, perhaps) - is it OK to send empty arrays?
BTW, you might want to dig into scipy/optimize/_linprog_highs.py and just:
return highs_wrapper(c, A, rhs, lhs, lb, ub, options=options)

for debugging purposes.

This looks like a good call to wrapper, I think it's just an unhandled error.  It looks like the model is unset when an error is encountered during optimization, so it's returning the contents of random pointers which is why we're getting garbage.
I can check for that in the wrapper, but I can't speak to why it's failing on such a simple problem.  It successfully solves the problem if you pass presolve=False using the simplex method, but method='ipm' always  fails on this problem.  I'll raise an issue over in HiGHS to see if they know why that problem would be failing.

Interesting. I thought I was giving something reasonable. Ok, I'll test with more involved problems.

Some HiGHS constants are now exposed to Python, for example:
from pyHiGHS.linprog import (
    HIGHS_CONST_I_INF,  # instead of integer infinity
    HIGHS_CONST_INF,  # instead of double np.inf
    HIGHS_CONST_TINY,  # instead of double -np.inf
    HIGHS_CONST_ZERO,  # instead of tol/eps
    HIGHS_THREAD_LIMIT,  # for max number of threads (linux only)
    ML_NONE,  # Verbosity levels
    ML_VERBOSE,
    ML_DETAILED,
    ML_MINIMAL,
    SOLVER_OPTION_CHOOSE,  # Default solver
    SOLVER_OPTION_SIMPLEX,  # simplex solver
    SOLVER_OPTION_IPM,  # interior-point solver
)

Use them like you would in subprocesses, e.g., Popen(..., stdout=subprocess.PIPE) -> lhs = pyHiGHS.HIGHS_CONST_TINY*np.ones((n,), dtype='double'), if that makes sense.
Windows build is now fixed (I haven't tried it, I suspect that OpenMP will have to be stripped out of setup.py for Windows).  You have to use MSYS2 like scipy builds.
I am working on making wheels for scikit-highs for faster, easier installation on both Windows and Linux while we integrate into scipy.  I've also removed all references to scipy for now in the project because I was having trouble with local builds, so make sure you cast to csc_matrix before passing into highs_wrapper, as it will not catch the error currently.

Some HiGHS constants are now exposed to Python:
Do I need to use them?

What's the next step do you think?
Getting this to pass all of the linprog tests. I'm going to do another force push to correct a few more issues with the initial commit. I would prefer to rewrite the history that I got it all right on my first try : ) Anyway, when that's done, try running:

python runtests.py -v -t scipy/optimize/tests/test_linprog.py

The very first test causes a fatal error so none of the rest even run, so I'm guessing that trying to make sure all ~70 pass will take a bit of work!

Is it the infinities? Do I need to use the ones you exposed?

Not necessarily (though I think it would be good practice).  I can get it to run that example by setting presolve=False.  It seems like there's a few strange things going on with presolve, so I'll raise an issue with HiGHS about presolve and let's default to presolve=False.

The very first test currently causes a fatal error so none of the rest even run, so I'm guessing that trying to make sure all ~70 pass will take a bit of work!

I'll start taking a look, hopefully turning off presolve will help.

I can get it to run that example by setting presolve=False

It runs, but it doesn't get the optimal answer. The correct solution is [2, 6].

Yep, that's a problem.  I'll take a look.

Gets the right answer if you feed it A.T.  Works with and without presolve.  Something seems fishy to me because that's not how the dimensions of A should work out, right?  I'll make notes on issue in HiGHS.

I am unable to get pytest to build scipy without errors.  Running pip install -e . builds fine, but running python runtests.py -v -t scipy/optimize/tests/test_linprog.py results in a build error.
Looks like a linking error:
collect2: error: ld returned 1 exit status
Running from SciPy source directory.
/venvs/scipy-highs/lib/python3.6/site-packages/numpy/distutils/system_info.py:820: UserWarning: Specified path /venvs/scipy-highs/include/python3.6m is invalid.
  return self.get_paths(self.section, key)
/venvs/scipy-highs/lib/python3.6/site-packages/numpy/distutils/system_info.py:820: UserWarning: Specified path /usr/local/include/python3.6m is invalid.
  return self.get_paths(self.section, key)

Any ideas?

Gets the right answer if you feed it A.T.

I had been assuming that they want matrices in CSC format. It's possible they are supposed to be in CSR format.
But on my machine the transpose fares no better...

Any ideas?

Hah yeah I got something like that early today, so I nuked and rebuilt my setup for the second time in 24 hours and afterwards it worked like a charm : ) I'm not really good at debugging these things.

Nuking almost worked, now it's broken on something else.  I'll pick this back up tomorrow, hopefully we'll have some feedback from the HiGHS team by then.

Easier to stop and start a docker container?

@andyfaff oops I should have suggested that. I haven't been using Docker since I got my Windows build working.

@mckib2  Did you really mean you just changed the line in _linprog_highs.py to:
res = highs_wrapper(c, A.transpose(), rhs, lhs, lb, ub, options=options)
and it worked?

I appreciate the push to get this into scipy. What's the next step do you think? Do I have access to this branch? If so, I could try embedding a submodule of either my fork or the main project with a patch file. @rgommers What needs to be done to my fork to make it suitable for inclusion as a submodule?

I think we need a structure like:

scipy/optimize/highs/setup.py  (not in submodule)
scipy/optimize/highs/highs (submodule dir)

OpenMP needs to be disabled unconditionally. For the rest, CI may highlight some issues.

Windows build is now fixed (I haven't tried it, I suspect that OpenMP will have to be stripped out of setup.py for Windows). You have to use MSYS2 like scipy builds.

It definitely would need to build with MSVC.

It definitely would need to build with MSVC.

This will supported in upcoming release it sounds like (see #279)

@mckib2 Did you really mean you just changed the line in _linprog_highs.py to:
res = highs_wrapper(c, A.transpose(), rhs, lhs, lb, ub, options=options)
and it worked?

I transposed A before converting to csc_matrix, but this results in a undefined variable which means it sometimes works and sometimes doesn't -- see issue #282.  They are pretty responsive and mentioned fails might be due to an infinity value.  I'll do some testing later today.

Easier to stop and start a docker container?

If I can't get it to work, seems like a good option.

Bug fix to C API pulled from HiGHS (pull request #6), scikit-highs==0.0.19 is solving the problem as expected.
Still haven't gotten scipy pytest to build, so I can't say if it works in the test suite.

OK, I think this is in a state that you can test. So far I've been messing around with test code like:
import numpy as np
from scipy.optimize import linprog
c = [-1, 1]
A_ub = [[1, 0]]
b_ub =[1]
A_eq = [[0, 1]]
b_eq =[2]
bounds = [[1, 1], [2, 2]]
res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method="highs-simplex")

This is now working for me with scikit-highs==0.0.19.

In case anyone else has problems getting tests to build, I had to upgrade to python3.7 and then build in place ala
python setup.py build_ext --inplace
before I could get any of the tests to run.  Doesn't seem to play nice with with python3.6.

I'm getting a segmentation fault in test test_bounds_equal_but_infeasible.  I've been getting mostly passing up to there by adding this to _linprog_highs.py:

    # HIGHSStatus -> linprog status
    status_mapper = {
        8: 3, # Unbounded
        9: 0, # Optimal
    }

    sol = {
        'x': res['col_value'],
        'slack': res['col_dual'],
        'fun': res['fun'],
        'con': res['sum_primal_infeasibilities'],
        'status': status_mapper[res['model_status']['status']],
        'success': res['model_status']['status'] == 9,
        'message': res['model_status']['message'],
        'nit': res['simplex_nit'] if solver == 'simplex' else res['ipm_nit'],
        }

I've raised an issue asking how to find out if a solution is unbounded, right now it's not doing the right thing (see #284).  There is a place it is being set, I just don't know how to access it.

Made those changes. Does it work for you?

  Latest scikit-highs avoids segfaults, I've resolved all my issues raised with HiGHS.

It doesn't solve all the errors, but I get significantly fewer when I ensure that A is actually a csc_matrix.  It gets turned into a coo_matrix during the vstack, so either add another csc_matrix or wait to convert to csc_matrix till after stacking.

With scikit-highs==0.0.23, only 73 errors left when ensuring A is always a csc_matrix.  There are less if you presolve is always set to False.  I'll have to start going through one by one to see what the issue is with the rest.

OpenMP needs to be disabled unconditionally.

OpenMP is disabled unconditionally.

It gets turned into a coo_matrix during the vstack

Oh good call. The reason I wanted to turn into CSC first was to avoid having separate logic for arrays that are passed into linprog as sparse, but it looks like it's for the best.

W00T! First time I've gotten sensible output!! : )
(scipydev) mdhaber@DESKTOP-C3ADILE:~$ python untitled0.py
untitled0.py:19: OptimizeWarning: Sparse constraint matrix detected; setting 'sparse':True.
  res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='highs-simplex')
untitled0.py:19: OptimizeWarning: Unknown solver options: sparse
  res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='highs-simplex')
c= [-3. -2.] , type=  <class 'numpy.ndarray'> dtype=  float64
A=
 [[2. 1.]
 [1. 1.]
 [1. 0.]] , type=  <class 'scipy.sparse.csc.csc_matrix'> dtype=  float64
rhs= [10.  8.  4.] , type=  <class 'numpy.ndarray'> dtype=  float64
lhs= [-inf -inf -inf] , type=  <class 'numpy.ndarray'> dtype=  float64
lb= [0. 0.] , type=  <class 'numpy.ndarray'> dtype=  float64
ub= [inf inf] , type=  <class 'numpy.ndarray'> dtype=  float64
options= {'presolve': False, 'sense': 1, 'solver': 'simplex', 'parallel': False, 'time_limit': 1, 'message_level': 1, 'write_solution_to_file': False, 'solution_file': 'test.sol', 'write_solution_pretty': True}
Solving
21:56:32 [INFO   ] Solving LP without presolve or with basis
21:56:32 [INFO   ] Scaling: Matrix has [min, max] values of [1, 2] within [0.2, 5] so no scaling performed
21:56:32 [INFO   ] Initial basis condition estimate of           1 is within the tolerance of 1e+14
21:56:32 [WARNING] Number of OMP threads available = 0 < 1 = Number of HiGHS threads to be used: Parallel performance will be less than anticipated
21:56:32 [INFO   ] Using dual simplex solver - serial
21:56:32 [INFO   ] Final basis condition estimate is 16
21:56:32 [INFO   ] HiGHS basic solution: Analysis - Solving LP without presolve or with basis
21:56:32 [INFO   ] HiGHS basic solution: 3 Simplex iterations; Objective = -18
21:56:32 [INFO   ] Infeasibilities: Pr 0(Max 0, Sum 0); Du 0(Max 0, Sum 0); Status: Optimal
{'fun': -18.0, 'simplex_nit': 3, 'ipm_nit': 0, 'crossover_nit': 0, 'primal_status': {'status': -1, 'message': 'Not set'}, 'dual_status': {'status': -1, 'message': 'Not set'}, 'num_primal_infeasibilities': 0, 'max_primal_infeasibility': 0.0, 'sum_primal_infeasibilities': 0.0, 'num_dual_infeasibilities': 0, 'max_dual_infeasibility': 0.0, 'sum_dual_infeasibilities': 0.0, 'col_value': array([2., 6.]), 'col_dual': array([0., 0.]), 'row_value': array([10.,  8.,  2.]), 'row_dual': array([1., 1., 0.]), 'col_basis_status': {'statuses': [1, 1], 'messages': ['BASIC: (slack) variable is basic', 'BASIC: (slack) variable is basic']}, 'row_basis_status': {'statuses': [2, 2, 1], 'messages': ['UPPER: (slack) variable is at its upper bound', 'UPPER: (slack) variable is at its upper bound', 'BASIC: (slack) variable is basic']}, 'model_status': {'status': 9, 'message': 'Optimal'}}
<class 'scipy.optimize.optimize.OptimizeResult'>
     con: 0.0
     fun: -18.0
 message: 'Optimal'
     nit: 3
   slack: array([0., 0.])
  status: 0
 success: True
       x: array([2., 6.])

Did you change something between 0.0.19 and 0.0.23 to make that one work? Even after changes to _linprog_highs, it wasn't working until I updated pyHiGHS this evening.

With resolve off, I'm getting 79 failed tests, not 73... but hopefully we'll get that down to zero on both of our computers... with resolve on and off. Right now:
scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSSimplex::test_bounds_equal_but_infeasible Fatal Python error: Segmentation fault

with presolve on.
A handful of these errors are just issues with options; I can fix those. I'll start taking a look at what's being passed into the wrapper for the rest.

@mckib2 Have you seen whether they have any options for scaling the problem? I'm trying to determine whether there's anything I can hook our autoscale option into, or whether I have to disable it.
Similarly, is there any way of controlling the maximum number of iterations?
I noticed that for the problem I had been testing with, the one with:
A_ub = [[2, 1], [1, 1], [1, 0]]

I'm seeing slack: array([0., 0.]). There should be three values because there are three inequality constraints. So I'm not sure that we can interpret res['col_dual'] as the slack. Is there another place to get it from HiGHS or should I just calculate it based on the res['x']?

I just notice that if I replace np.inf with a large number, it solves at least one problem it couldn't before... Maybe I'll try using those constants you suggested.

Great! Only 39 failing now. That's enough for tonight. One thing though - you wrote:
HIGHS_CONST_TINY,  # instead of double -np.inf

That doesn't look right, and indeed, it's like 1e-14, not negative infinity. I am just using:
-HIGHS_CONST_INF

if that's OK.

One last thing for the night - on unbounded problems, even using highs-simplex:
RuntimeError: Model failed during optimization! Could be unbounded! Try `presolve=False` and/or `method='simplex'

It's raising that instead of returning status 8. I think that would fix most of the remaining problems.

test_bug_8973 was too specific; it will pass now for simplex. For ipm, though, the objective function value is not consistent with the solution vector.
from scipy.optimize import linprog
import numpy as np

c = np.array([0, 0, 0, 1, -1])
A_ub = np.array([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0]])
b_ub = np.array([2, -2])
bounds = [(None, None), (None, None), (None, None), (-1, 1), (-1, 1)]
res = linprog(c, A_ub, b_ub, bounds=bounds, method='highs-ipm')
print(res)

Output is
(scipydev) mdhaber@DESKTOP-C3ADILE:~$ python untitled1.py
c= [ 0.  0.  0.  1. -1.] , type=  <class 'numpy.ndarray'> dtype=  float64
A=
 [[1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]] , type=  <class 'scipy.sparse.csc.csc_matrix'> dtype=  float64
rhs= [ 2. -2.] , type=  <class 'numpy.ndarray'> dtype=  float64
lhs= [-1.e+200 -1.e+200] , type=  <class 'numpy.ndarray'> dtype=  float64
lb= [-1.e+200 -1.e+200 -1.e+200 -1.e+000 -1.e+000] , type=  <class 'numpy.ndarray'> dtype=  float64
ub= [1.e+200 1.e+200 1.e+200 1.e+000 1.e+000] , type=  <class 'numpy.ndarray'> dtype=  float64
options= {'presolve': False, 'sense': 1, 'solver': 'ipm', 'parallel': False, 'time_limit': 1, 'message_level': 0, 'write_solution_to_file': False, 'solution_file': 'test.sol', 'write_solution_pretty': True}
IPX version 1.0
Input
    Number of variables:                                5
    Number of free variables:                           3
    Number of constraints:                              2
    Number of equality constraints:                     0
    Number of matrix entries:                           2
    Matrix range:                                       [1e+00, 1e+00]
    RHS range:                                          [2e+00, 2e+00]
    Objective range:                                    [1e+00, 1e+00]
    Bounds range:                                       [1e+00, 1e+00]
Preprocessing
    Dualized model:                                     no
    Number of dense columns:                            0
    Range of scaling factors:                           [1.00e+00, 1.00e+00]
Interior Point Solve
 Iter     P.res    D.res            P.obj           D.obj        mu     Time  stepsizes   pivots kktiter  P.fixed D.fixed
   0   3.99e+00 2.61e+00   0.00000000e+00 -1.04250000e+01  1.21e+01       0s  0.00 0.00        0       1        0       0
   1   3.99e-06 1.07e+00  -4.57767859e-01 -6.21151604e+00  1.55e+00       0s  1.00 1.00        0       2        0       0
   2   3.99e-12 1.23e-01  -1.38895275e+00 -1.96092775e+00  1.64e-01       0s  1.00 0.89        0       2        0       0
   3   7.11e-15 7.19e-06  -1.99996078e+00 -2.00115263e+00  2.03e-04       0s  1.00 1.00        0       2        0       0
   4*  5.45e-17 7.19e-12  -2.00000000e+00 -2.00000000e+00  2.03e-10       0s  1.00 1.00        0       2        0       0
 Constructing starting basis...
    Number of free variables in starting basis:         2
    Number of singletons in starting basis:             0
    Number of other columns matched:                    0
    Number of other columns failed:                     1
    Number of columns dropped from guessed basis:       0
    Minimum singular value of crash basis:              1.00e+00
    Number of free variables nonbasic:                  1
    Number of free variables swapped for stability:     0
    Number of fixed variables basic:                    0
    Number of fixed variables swapped for stability:    0
Crossover
    Primal residual before push phase:                  0.00e+00
    Dual residual before push phase:                    7.19e-12
    Number of dual pushes required:                     0
    Number of primal pushes required:                   2
    Primal residual after push phase:                   0.00e+00
    Dual residual after push phase:                     7.19e-12
    Bound violation of basic solution:                  0.00e+00
    Dual sign violation of basic solution:              0.00e+00
    Minimum singular value of basis matrix:             1.00e+00
Summary
    Runtime:                                            0.15s
    Status interior point solve:                        optimal
    Status crossover:                                   optimal
    objective value:                                    -2.00000000e+00
    interior solution primal residual (abs/rel):        1.67e-16 / 5.55e-17
    interior solution dual residual (abs/rel):          7.19e-12 / 3.60e-12
    interior solution objective gap (abs/rel):          1.19e-09 / 3.97e-10
    basic solution primal infeasibility:                0.00e+00
    basic solution dual infeasibility:                  0.00e+00
{'fun': -2.0, 'simplex_nit': 0, 'ipm_nit': 4, 'crossover_nit': 0, 'primal_status': {'status': -1, 'message': 'Not set'}, 'dual_status': {'status': -1, 'message': 'Not set'}, 'num_primal_infeasibilities': 1, 'max_primal_infeasibility': 2.0, 'sum_primal_infeasibilities': 2.0, 'num_dual_infeasibilities': 0, 'max_dual_infeasibility': 0.0, 'sum_dual_infeasibilities': 0.0, 'col_value': array([ 2., -2.,  0.,  0.,  0.]), 'col_dual': array([0., 0., 0., 0., 0.]), 'row_value': array([0., 0.]), 'row_dual': array([0., 0.]), 'col_basis_status': {'statuses': [1, 1, 0, 0, 0], 'messages': ['BASIC: (slack) variable is basic', 'BASIC: (slack) variable is basic', 'LOWER: (slack) variable is at its lower bound [including fixed variables]', 'LOWER: (slack) variable is at its lower bound [including fixed variables]', 'LOWER: (slack) variable is at its lower bound [including fixed variables]']}, 'row_basis_status': {'statuses': [0, 0], 'messages': ['LOWER: (slack) variable is at its lower bound [including fixed variables]', 'LOWER: (slack) variable is at its lower bound [including fixed variables]']}, 'model_status': {'status': 9, 'message': 'Optimal'}}
     con: 2.0
     fun: -2.0
 message: 'Optimal'
     nit: 4
   slack: array([0., 0., 0., 0., 0.])
  status: 0
 success: True
       x: array([ 2., -2.,  0.,  0.,  0.])

[ 2., -2.,  0.,  0.,  0.] @ [ 0.  0.  0.  1. -1.] is not -2

test_zero_column_2 should pass when we let the presolve option control whether presolve is actually performed, because I check that the problem is solved in presolve (nit == 0). Same with test_singleton_row_ub_1, test_singleton_row_eq_1,  test_zero_row_3, and test_zero_column_2.
test_unbounded_no_nontrivial_constraints_1 and test_unbounded_no_nontrivial_constraints_2 should pass if I ensure that fun is negative infinity. I'm also adjusting those tests so they don't require a specific status message from the HiGHS solvers.
I think that addresses all of the tests that don't fail with:
RuntimeError: Model failed during optimization! Could be unbounded! Try `presolve=False` and/or `method='simplex'

For simplex, all of those - , test_unbounded_below_no_presolve_corrected, test_bounds_simple, test_empty_constraint_1, test_empty_constraint_2, and test_empty_constraint_3 - are silly corner cases with bound constraints only. I imagine they'd pass with presolve on, but as it is they fail. Do you think it's worth mentioning to the HiGHS folks that these sorts problems fail without presolve, or should I just write something in to address unconstrained problems?

For the ones that fail differently for IPM, I assume that most are difficulties with unbounded or infeasible problems. test_bug_8664 is interesting because it's a simple problem that my own IPM solver had trouble with without presolve. But maybe this will be fixed when we get presolve working.

Summary for @mckib2:

Have you seen whether there are any options for scaling the problem?
Have you seen whether there is an option for controlling the max number of iterations?
What should I use to represent -inf, if not -HIGHS_CONST_INF? You wrote HIGHS_CONST_TINY, but that's a small number.
Please take a look at the post about test_bug_8973 above. The results for IPM are weird.
Problems with bound constraints only fail with a RuntimeWarning - but of course they are trivial problems that would be solved by a presolve routine. Still, I tried to make the linprog solvers handle these sorts of problems because people who are learning SciPy sometimes test with trivial inputs, and failures for these don't look good. Is it worth asking the HiGHS developers to address these sorts of trivial problems in their code (without presolve), or should we handle it?
Basically all the problems that are failing could be fixed by presolve, so please let me know how I can help you get that working!

Have you seen whether there are any options for scaling the problem?

Whoops - missed this one in the wrapper options.  There are several scaling strategies, see docs.  It looks like you can choose from the following (I'm not sure what they all do, it appears undocumented):
enum SimplexScaleStrategy {
  SIMPLEX_SCALE_STRATEGY_MIN = 0,
  SIMPLEX_SCALE_STRATEGY_OFF = SIMPLEX_SCALE_STRATEGY_MIN,
  SIMPLEX_SCALE_STRATEGY_HIGHS,
  SIMPLEX_SCALE_STRATEGY_HIGHS_FORCED,
  SIMPLEX_SCALE_STRATEGY_HIGHS_015,
  SIMPLEX_SCALE_STRATEGY_HIGHS_0157,
  SIMPLEX_SCALE_STRATEGY_HSOL,
  SIMPLEX_SCALE_STRATEGY_MAX = SIMPLEX_SCALE_STRATEGY_HSOL
};

Looks like best bet is to say 0 or 1 for on and off.  I'll have to add that to the accepted wrapper options.

Have you seen whether there is an option for controlling the max number of iterations?

Yes, see here.  Just set the option:
options = {
    ...
    'simplex_iteration_limit': myIntLimit,
    ...
}

What should I use to represent -inf, if not -HIGHS_CONST_INF? You wrote HIGHS_CONST_TINY, but that's a small number.

That was my mistake, use -HIGHS_CONST_INF.  Good catch.

Please take a look at the post about test_bug_8973 above. The results for IPM are weird.

I'll check and see if converting to MPS and running with HiGHS proper works.

Problems with bound constraints only fail with a RuntimeWarning - but of course they are trivial problems that would be solved by a presolve routine. Still, I tried to make the linprog solvers handle these sorts of problems because people who are learning SciPy sometimes test with trivial inputs, and failures for these don't look good. Is it worth asking the HiGHS developers to address these sorts of trivial problems in their code (without presolve), or should we handle it?

I think the HiGHS solver does handle these pretty well (from what I've tested).  I've built the wrappers on top of the C API which I have learned is not fully featured and does have some bugs in it.  I'm spending a little bit of time migrating to the fully featured C++ API.  That will be better for long term maintainability as well.

Basically all the problems that are failing could be fixed by presolve, so please let me know how I can help you get that working!

I think some of these might be solved by moving away from C API as described above

simplex_iteration_limit
Is there an equivalent for IPM?

'slack': res['col_dual']
This doesn't seem like the slack of the inequality constraints from the original problem. Is there another place to get it from HiGHS or should I just calculate it based on the res['x'] (b_ub - A_ub @ x)?

'con': res['sum_primal_infeasibilities'],
Similarly, here. I'm looking for b_eq - A_eq @ x

Is there an equivalent for IPM?

There does not appear to be, but I asked (see issue #286).  I also noted that setting it appears to do nothing -- I was running into the same issue.

This doesn't seem like the slack of the inequality constraints from the original problem. Is there another place to get it from HiGHS or should I just calculate it based on the res['x'] (b_ub - A_ub @ x)?

I looked at all the different outputs and didn't see where slack is (I assume it must be there somewhere?).  I think for now just calculating it will be the best solution until we find how to get it.

Similarly, here. I'm looking for b_eq - A_eq @ x

Opinion is same as above for slack.

Alright...  Lots of changes with new wrapper based on C++ API.  But it works better.
Everything with options should stay the same.  Added support for simplex_scale_strategy.
Function signature is a little bit different:
def highs_wrapper(
        double[::1] c,
        int[::1] astart,
        int[::1] aindex,
        double[::1] avalue,
        double[::1] lhs,
        double[::1] rhs,
        double[::1] lb,
        double[::1] ub,
        dict options):

Note that the order of lhs and rhs is not as before (this way makes more sense to me, listing the left hand side first).  A is not passed in, rather it's properties are, e.g.:
res = highs_wrapper(
    c,
    A.indptr,
    A.indices,
    A.data,
    lhs,
    rhs,
    lb,
    ub,
    options)

The result dictionary is also different, this can be adjusted if needed.  It only returns information if it is available from HiGHS.  This means that if a problem is not solved only the status code and message are returned.  If a problem is able to be solved, the, more  information returned.  Below is a list of everything that is returned under what circumstances.
If the problem was solved (status in [OPTIMAL, REACHED_DUAL_OBJECTIVE_VALUE_UPPER_BOUND, REACHED_TIME_LIMIT, REACHED_ITERATION_LIMIT]):
        return {
            'status': <int> model_status,
            'message': highs.highsModelStatusToString(model_status).decode(),
            'x': [solution.get().col_value[ii] for ii in range(numcol)],
            'slack': [solution.get().row_value[ii] for ii in range(numrow)],
            'fun': info.objective_function_value,
            'simplex_nit': info.simplex_iteration_count,
            'ipm_nit': info.ipm_iteration_count,
            'crossover_nit': info.crossover_iteration_count,
            'con': info.sum_primal_infeasibilities,
        }

Again, I do not know where slack actually is, so that field may be ignored (as well as con).  Note that x is returned as a list, so it may be necessary to cast as numpy array.
If the problem cannot be solved (status in [NOTSET, LOAD_ERROR, MODEL_ERROR, MODEL_EMPTY, PRESOLVE_ERROR, SOLVE_ERROR, POSTSOLVE_ERROR, PRIMAL_INFEASIBLE, PRIMAL_UNBOUNDED]):
        return {
            'status': <int> model_status,
            'message': highs.highsModelStatusToString(model_status).decode(),
        }

Also note that the only constants being exported are:
CONST_INF # double infinity
CONST_I_INF # integer infinity

What does this change?

Change constant imports to from pyHIGHS import CONST_INF, CONST_I_INF
Remove extra constant imports
Change instances of HIGHS_CONST_INF to CONST_INF
Modify construction of result:

    sol = {'x': res.get('x', None),
           'slack': res.get('slack', None),
           'fun': res.get('fun', None),
           'con': res.get('con', None),
           'status': statuses[res['status']][0],
           'success': res['status'] == 9,
           'message': res['message'],
           'nit': (res.get('simplex_nit', 0) if solver == 'simplex'
                   else res.get('ipm_nit', 0)),
           }
    if sol['x'] is not None:
        sol['x'] = np.array(sol['x'])

Making those changes I get 21 failing on my machine.  Most of these (17 of them) appear to be due to IPM not returning the correct status code (gives key error).  I'm looking into this.

Callback seems planned, no movement on it yet it looks like: see issue #112

For simplex, all of those - , test_unbounded_below_no_presolve_corrected, test_bounds_simple, test_empty_constraint_1, test_empty_constraint_2, and test_empty_constraint_3 - are silly corner cases with bound constraints only. I imagine they'd pass with presolve on, but as it is they fail. Do you think it's worth mentioning to the HiGHS folks that these sorts problems fail without presolve, or should I just write something in to address unconstrained problems?

These are now passing with new wrapper, let me know if they're still failing for you.

For the ones that fail differently for IPM, I assume that most are difficulties with unbounded or infeasible problems. test_bug_8664 is interesting because it's a simple problem that my own IPM solver had trouble with without presolve. But maybe this will be fixed when we get presolve working.

This test is now passing with new wrapper

Please take a look at the post about test_bug_8973 above. The results for IPM are weird.

I get the following with new wrapper:
c = [ 0,  0,  0,  1, -1]
x = [ 0., -2.,  0., -1.,  1.]
desired_x = [ 2, -2,  0, -1,  1]
fun = c @ x = -2 #  is correct?

Conditions are:
-inf <= x[0] <= inf  # from lb, ub
x[0] <= 2  # from A_ub

So are both solutions correct?  As long as x[0] <= 2, it doesn't change the function value, so 0 is as good as 2?

So are both solutions correct?

Yes. When I wrote that "test_bug_8973 was too specific", I had already changed the test not to check for the solution vector, so simplex was passing. The problem was that the solution IPM was producing was not consistent with the objective function value it reported.

Not building the latest version:
(scipydev) mdhaber@DESKTOP-C3ADILE:~/HiGHS$ git fetch origin
(scipydev) mdhaber@DESKTOP-C3ADILE:~/HiGHS$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
(scipydev) mdhaber@DESKTOP-C3ADILE:~/HiGHS$ python setup.py build_ext --inplace
Traceback (most recent call last):
  File "setup.py", line 87, in <module>
    with open('GITHASH', 'r') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'GITHASH'

Latest (0.0.27) should build

No luck. https://www.dropbox.com/s/zv2n3v8sjb7jotx/HiGHS Build Log.txt?dl=0

Well shoot.  I'll take a look at it.  You will, however, be pleased to note that I have a patch submitted to fix Windows build (for cmake at least): see #289.  Once cmake build is working I'll be able to translate to pip install.

Yes! That would be great.

Alright, I think I see what happened.  I wrote a wrapper over their mps writer so I could do some testing and it never got the c++14 flag.  I've removed the mpswriter from the build, so (fingers-crossed) it should work now.  0.0.28 should be up now

@mckib2 When you get a chance, could you take a closer look at the problem here. Even in the latest version, IPM is giving a solution vector that is inconsistent with the function value.

Can you write the problem as an MPS file?

Simplex is passing all tests. IPM fails all for the same reason:
_______________________________________________________________ TestLinprogHiGHSIPM.test_bug_8664 ________________________________________________________________
scipy/optimize/tests/test_linprog.py:1263: in test_bug_8664
    method=self.method, options=self.options)
        A_eq       = [[0], [-8], [9]]
        A_ub       = [[2], [5]]
        b_eq       = [3, 2, 10]
        b_ub       = [4, 4]
        c          = [4]
        self       = <scipy.optimize.tests.test_linprog.TestLinprogHiGHSIPM object at 0x7fb33a340550>
        sup        = <numpy.testing._private.utils.suppress_warnings object at 0x7fb33a3407d0>
scipy/optimize/_linprog.py:526: in linprog
    **solver_options)
        A_eq       = [[0], [-8], [9]]
        A_ub       = [[2], [5]]
        b_eq       = [3, 2, 10]
        b_ub       = [4, 4]
        bounds     = None
        c          = [4]
        callback   = None
        highs_solvers = {'highs-ipm': 'ipm', 'highs-simplex': 'simplex'}
        lp         = _LPProblem(c=array([4.]), A_ub=array([[2.],
       [5.]]), b_ub=array([4., 4.]), A_eq=array([[ 0.],
       [-8.],
       [ 9.]]), b_eq=array([ 3.,  2., 10.]), bounds=array([[ 0., inf]]), x0=None)
        meth       = 'highs-ipm'
        method     = 'highs-ipm'
        options    = {}
        solver_options = {}
        x0         = None
scipy/optimize/_linprog_highs.py:207: in _linprog_highs
    'status': statuses[res['status']][0],
E   KeyError: 0
        A          = <5x1 sparse matrix of type '<class 'numpy.float64'>'
        with 4 stored elements in Compressed Sparse Column format>
        A_eq       = array([[ 0.],
       [-8.],
       [ 9.]])
        A_ub       = array([[2.],
       [5.]])
        autoscale  = False
        b_eq       = array([ 3.,  2., 10.])
        b_ub       = array([4., 4.])
        bounds     = array([[ 0., inf]])
        c          = array([4.])
        disp       = False
        lb         = array([0.])
        lhs        = array([-1.e+200, -1.e+200,  3.e+000,  2.e+000,  1.e+001])
        lhs_eq     = array([ 3.,  2., 10.])
        lhs_ub     = array([-inf, -inf])
        lp         = _LPProblem(c=array([4.]), A_ub=array([[2.],
       [5.]]), b_ub=array([4., 4.]), A_eq=array([[ 0.],
       [-8.],
       [ 9.]]), b_eq=array([ 3.,  2., 10.]), bounds=array([[ 0., inf]]), x0=None)
        maxiter    = None
        options    = {'message_level': 0, 'parallel': False, 'presolve': False, 'sense': 1, ...}
        parallel   = False
        presolve   = False
        res        = {'message': 'Not Set', 'status': 0}
        rhs        = array([ 4.,  4.,  3.,  2., 10.])
        rhs_eq     = array([ 3.,  2., 10.])
        rhs_ub     = array([4., 4.])
        solver     = 'ipm'
        statuses   = {7: (2, 'The problem is infeasible.'), 8: (3, 'The problem is unbounded.'), 9: (0, 'Optimization terminated successfully.')}
        time_limit = 1
        ub         = array([1.e+200])
        unknown_options = {}
        x0         = None
---------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------
1e+200
[1. 1. 1. 1. 1.]
1e+200
[-1. -1.  1.  1.  1.]
1e+200
[0.]
1e+200
[1.]
c= [4.] , type=  <class 'numpy.ndarray'> dtype=  float64
A=
 [[ 2.]
 [ 5.]
 [ 0.]
 [-8.]
 [ 9.]] , type=  <class 'scipy.sparse.csc.csc_matrix'> dtype=  float64
rhs= [ 4.  4.  3.  2. 10.] , type=  <class 'numpy.ndarray'> dtype=  float64
lhs= [-1.e+200 -1.e+200  3.e+000  2.e+000  1.e+001] , type=  <class 'numpy.ndarray'> dtype=  float64
lb= [0.] , type=  <class 'numpy.ndarray'> dtype=  float64
ub= [1.e+200] , type=  <class 'numpy.ndarray'> dtype=  float64
options= {'presolve': False, 'sense': 1, 'solver': 'ipm', 'parallel': False, 'time_limit': 1, 'message_level': 0, 'write_solution_to_file': False, 'solution_file': 'test.sol', 'write_solution_pretty': True}
IPX version 1.0
Input
    Number of variables:                                1
    Number of free variables:                           0
    Number of constraints:                              5
    Number of equality constraints:                     3
    Number of matrix entries:                           4
    Matrix range:                                       [2e+00, 9e+00]
    RHS range:                                          [2e+00, 1e+01]
    Objective range:                                    [4e+00, 4e+00]
    Bounds range:                                       [0e+00, 0e+00]
Preprocessing
    Dualized model:                                     yes
    Number of dense columns:                            0
    Range of scaling factors:                           [5.00e-01, 1.00e+00]
Interior Point Solve
 Iter     P.res    D.res            P.obj           D.obj        mu     Time  stepsizes   pivots kktiter  P.fixed D.fixed
   0   2.09e+00 3.69e+00  -2.68592058e+00 -2.68592058e+00  7.36e+00       0s  0.00 0.00        0       2        0       0
   1   2.09e-06 3.00e+00  -1.61000558e+01 -1.79149598e+00  8.34e-01       0s  1.00 1.00        0       2        0       0
   2   1.52e-08 3.00e+00  -5.42504128e+01 -2.00091179e+00  1.09e-02       0s  0.99 1.00        0       2        0       0
   3   6.56e-14 3.04e+00  -2.39113651e+03 -2.04146998e+00  8.85e-06       0s  1.00 1.00        0       2        0       0
   4   8.30e-11 3.04e+00  -2.88994203e+06 -2.04137931e+00  5.87e-10       0s  1.00 1.00        0       2        0       0
   5   1.91e-06 3.04e+00  -4.35379501e+10 -2.04137931e+00  1.14e-11       0s  1.00 1.00        0       2        0       0
   6   3.34e-11 3.04e+00  -2.29338050e+12 -2.04137931e+00  1.89e-11       0s  1.00 1.00        0       2        0       0
   7   1.22e-04 3.04e+00  -3.64401594e+12 -2.04137931e+00  1.41e-11       0s  1.00 1.00        0       2        0       0
   8   3.34e-11 3.04e+00  -5.45878013e+12 -2.04137931e+00  1.63e-11       0s  1.00 1.00        0       2        0       0
   9   3.34e-11 3.04e+00  -7.02873189e+12 -2.04137931e+00  1.50e-11       0s  1.00 1.00        0       2        0       0
  10   2.44e-04 3.04e+00  -8.72797119e+12 -2.04137931e+00  1.57e-11       0s  1.00 1.00        0       2        0       0
  11   3.34e-11 3.04e+00  -1.03587096e+13 -2.04137931e+00  1.53e-11       0s  1.00 1.00        0       2        0       0
  12   4.88e-04 3.04e+00  -1.20257157e+13 -2.04137931e+00  1.55e-11       0s  1.00 1.00        0       2        0       0
  13   3.34e-11 3.04e+00  -1.36735070e+13 -2.04137931e+00  1.54e-11       0s  1.00 1.00        0       2        0       0
  14   1.52e-04 3.04e+00  -1.53313625e+13 -2.04124059e+00  3.77e+07       0s  1.00 1.00        0       2        0       0
  15   5.96e-05 3.26e+00  -1.53323785e+13 -2.26008803e+00  1.22e+06       0s  0.98 1.00        0       2        0       0
  16   4.60e-04 3.61e+00  -1.53324287e+13 -2.60958160e+00  1.03e+05       0s  1.00 1.00        0       2        0       0
  17   4.05e-04 3.47e+00  -1.53324413e+13 -2.46963874e+00  6.48e+02       0s  0.95 1.00        0       2        0       0
  18   5.01e-04 3.02e+00  -1.53324414e+13 -2.02183219e+00  3.32e+01       0s  1.00 1.00        0       2        0       0
  19   4.74e-04 3.12e+00  -1.53324414e+13 -2.12238224e+00  4.39e-01       0s  0.99 1.00        0       2        0       0
  20   3.58e-04 3.25e+00  -1.53324414e+13 -2.25114328e+00  1.02e-02       0s  1.00 1.00        0       2        0       0
  21   4.83e-04 3.04e+00  -1.53324414e+13 -2.04150914e+00  5.76e-04       0s  1.00 1.00        0       2        0       0
  22   4.88e-04 3.04e+00  -1.53324414e+13 -2.04137963e+00  3.39e-09       0s  1.00 1.00        0       2        0       0
  23   4.88e-04 3.04e+00  -1.53399918e+13 -2.04137931e+00  9.83e-12       0s  1.00 1.00        0       2        0       0
  24   3.53e-05 3.04e+00  -1.79385291e+13 -2.03963845e+00  7.42e+08       0s  1.00 1.00        0       2        0       0
  25   3.73e-04 3.26e+00  -1.79585433e+13 -2.25976462e+00  2.41e+07       0s  0.98 1.00        0       2        0       0
  26   4.68e-04 3.61e+00  -1.79595346e+13 -2.60960946e+00  2.05e+06       0s  1.00 1.00        0       2        0       0
  27   5.16e-04 3.47e+00  -1.79597853e+13 -2.46961540e+00  1.28e+04       0s  0.95 1.00        0       2        0       0
  28   3.58e-04 3.02e+00  -1.79597856e+13 -2.02127823e+00  6.60e+02       0s  1.00 1.00        0       2        0       0
  29   1.04e-05 3.12e+00  -1.79597856e+13 -2.12252697e+00  8.78e+00       0s  0.99 1.00        0       2        0       0
  30   5.84e-04 3.61e+00  -1.79597856e+13 -2.60972456e+00  1.16e+00       0s  1.00 1.00        0       2        0       0
  31   1.77e-04 3.47e+00  -1.79597856e+13 -2.46579887e+00  7.78e-03       0s  0.95 1.00        0       2        0       0
  32   1.49e-04 3.04e+00  -1.79597856e+13 -2.04136719e+00  6.94e-04       0s  1.00 1.00        0       2        0       0
  33   6.36e-10 3.04e+00  -1.79597856e+13 -2.04137952e+00  6.94e-10       0s  1.00 1.00        0       2        0       0
  34   6.28e-06 3.04e+00  -1.79966357e+13 -2.04137931e+00  5.69e-01       0s  1.00 1.00        0       2        0       0
  35   1.86e-04 3.12e+00  -1.79966357e+13 -2.11706247e+00  3.50e-03       0s  1.00 1.00        0       2        0       0
  36   4.89e-04 3.04e+00  -1.79966357e+13 -2.04139024e+00  1.32e-05       0s  1.00 1.00        0       2        0       0
  37   4.88e-04 3.04e+00  -1.79966377e+13 -2.04137931e+00  3.75e-11       0s  1.00 1.00        0       2        0       0
  38   4.86e-04 3.04e+00  -1.86782991e+13 -2.04137218e+00  7.96e+05       0s  1.00 1.00        0       2        0       0
  39   1.03e-03 3.26e+00  -1.86783206e+13 -2.26011462e+00  2.57e+04       0s  0.98 1.00        0       2        0       0
  40   5.68e-05 3.61e+00  -1.86783216e+13 -2.60957930e+00  2.18e+03       0s  1.00 1.00        0       2        0       0
  41   5.69e-04 3.47e+00  -1.86783219e+13 -2.46964066e+00  1.37e+01       0s  0.95 1.00        0       2        0       0
  42   4.53e-04 3.02e+00  -1.86783219e+13 -2.02186444e+00  7.01e-01       0s  1.00 1.00        0       2        0       0
  43   3.78e-04 3.11e+00  -1.86783219e+13 -2.10513230e+00  6.68e-03       0s  0.99 1.00        0       2        0       0
  44   9.77e-04 3.04e+00  -1.86783219e+13 -2.04142785e+00  2.22e-05       0s  1.00 1.00        0       2        0       0
  45   1.32e-10 3.04e+00  -1.86783231e+13 -2.04137932e+00  6.68e-09       0s  1.00 1.00        0       2        0       0
  46   9.75e-04 3.04e+00  -1.86821509e+13 -2.04137931e+00  1.53e-06       0s  1.00 1.00        0       2        0       0
  47   2.12e-11 3.04e+00  -1.86821676e+13 -2.04137931e+00  2.37e-10       0s  1.00 1.00        0       2        0       0
  48   2.12e-12 3.04e+00  -1.86821681e+13 -2.04137931e+00  2.31e-10       0s  0.00 1.00        0       2        0       0
  49   2.12e-13 3.04e+00  -1.86821682e+13 -2.04137931e+00  2.30e-10       0s  0.00 1.00        0       2        0       0
  50   9.77e-04 3.04e+00  -1.86821682e+13 -2.04137931e+00  2.30e-10       0s  0.00 1.00        0       2        0       0
  51   4.45e-04 3.04e+00  -1.87932413e+13 -2.04137931e+00  4.13e+01       0s  1.00 1.00        0       2        0       0
  52   3.71e-04 3.26e+00  -1.87932413e+13 -2.26011388e+00  1.33e+00       0s  0.98 1.00        0       2        0       0
  53   2.70e-04 3.61e+00  -1.87932413e+13 -2.60960801e+00  1.13e-01       0s  1.00 1.00        0       2        0       0
  54   4.06e-04 3.05e+00  -1.87932413e+13 -2.04868466e+00  1.37e-01       0s  1.00 1.00        0       2        0       0
  55   9.77e-04 3.06e+00  -1.87932413e+13 -2.06296137e+00  5.11e-05       0s  1.00 1.00        0       2        0       0
  56   9.77e-04 3.04e+00  -1.87932418e+13 -2.04137935e+00  1.86e-08       0s  1.00 1.00        0       2        0       0
  57   9.77e-04 3.04e+00  -1.87946137e+13 -2.04137931e+00  3.38e-11       0s  1.00 1.00        0       2        0       0
  58   9.77e-04 3.04e+00  -1.87946162e+13 -2.04137931e+00  1.93e-14       0s  0.00 1.00        0       2        0       0
  59   1.83e-15 3.04e+00  -1.34362426e+15 -2.04137931e+00  6.23e-16       0s  1.00 1.00        0       2        0       0
  60   2.00e+00 3.04e+00  -4.23836418e+16 -2.04137931e+00  1.04e-15       0s  1.00 1.00        0       2        0       0
  61   3.52e+13 4.77e+00   8.79636071e+29 -2.04137931e-01  3.90e+29       0s  1.00 0.00        0       2        0       0
  62   4.50e+15 3.90e+00  -5.39386420e+31 -2.90041379e+00  8.40e+30       0s  1.00 0.85        0       2        0       0
  63   1.58e+16 3.26e+00  -3.61264274e+32 -1.55100318e+00  5.79e+30       0s  1.00 1.00        0       2        0       0
  64   2.81e+15 3.28e+00  -5.40474712e+32 -2.27503709e+00  6.73e+29       0s  1.00 1.00        0       2        0       0
  65   2.19e+16 3.53e+00  -5.69406890e+32 -2.53442055e+00  5.17e+28       0s  0.98 1.00        0       2        0       0
  66   3.58e+15 3.50e+00  -5.74876998e+32 -2.50496901e+00  5.18e+25       0s  0.99 1.00        0       2        0       0
  67   1.10e+16 3.00e+00  -5.74878305e+32 -1.95124050e+00  4.82e+24       0s  1.00 1.00        0       2        0       0
  68   1.02e+15 3.16e+00  -5.74878488e+32 -2.16369155e+00  1.42e+23       0s  0.98 1.00        0       2        0       0
  69   1.06e+15 3.61e+00  -5.74878494e+32 -2.60667885e+00  1.72e+22       0s  1.00 1.00        0       2        0       0
  70   9.95e+15 3.47e+00  -5.74878496e+32 -2.46820192e+00  1.08e+20       0s  0.95 1.00        0       2        0       0
  71   1.78e+16 3.00e+00  -5.74878496e+32 -1.89228728e+00  1.19e+19       0s  1.00 1.00        0       2        0       0
  72   1.25e+16 3.13e+00  -5.74878496e+32 -2.12883282e+00  3.45e+17       0s  0.99 1.00        0       2        0       0
  73   1.18e+16 3.62e+00  -5.74878496e+32 -2.61822483e+00  4.33e+16       0s  1.00 1.00        0       2        0       0
  74   4.74e+14 3.45e+00  -5.74878496e+32 -2.45040790e+00  3.40e+14       0s  0.95 1.00        0       2        0       0
  75   1.68e+13 3.17e+00  -5.74878496e+32 -2.17478162e+00  9.68e+12       0s  1.00 1.00        0       2        0       0
  76   8.07e+09 3.28e+00  -5.74878496e+32 -2.27657101e+00  9.65e+10       0s  0.99 1.00        0       2        0       0
  77   8.30e+08 3.59e+00  -5.74878496e+32 -2.59291395e+00  9.27e+09       0s  1.00 1.00        0       2        0       0
  78   4.54e+07 3.48e+00  -5.74878496e+32 -2.48056365e+00  3.72e+07       0s  0.96 1.00        0       2        0       0
  79   1.62e+06 3.21e+00  -5.74878496e+32 -2.21404045e+00  9.10e+05       0s  1.00 1.00        0       2        0       0
  80   5.82e+02 3.29e+00  -5.74878496e+32 -2.28900029e+00  6.98e+03       0s  0.99 1.00        0       2        0       0
  81   5.71e+01 3.59e+00  -5.74878496e+32 -2.58671629e+00  6.66e+02       0s  1.00 1.00        0       2        0       0
  82   2.89e+00 3.48e+00  -5.74878496e+32 -2.48342831e+00  2.27e+00       0s  0.96 1.00        0       2        0       0
  83   1.12e-01 3.20e+00  -5.74878496e+32 -2.20118886e+00  6.54e-02       0s  1.00 1.00        0       2        0       0
  84   7.06e-04 3.04e+00  -5.74878496e+32 -2.04312947e+00  8.95e-04       0s  1.00 1.00        0       2        0       0
  85   1.22e-09 3.04e+00  -5.74878496e+32 -2.04137995e+00  2.47e-09       0s  1.00 1.00        0       2        0       0
  86   1.22e-09 3.04e+00  -5.74878496e+32 -2.04137931e+00  4.24e-10       0s  1.00 1.00        0       2        0       0
  87   1.22e-09 3.04e+00  -5.74878496e+32 -2.04137931e+00  6.84e-10       0s  1.00 1.00        0       2        0       0
  88   1.22e-09 3.04e+00  -5.74878496e+32 -2.04137931e+00  5.19e-10       0s  1.00 1.00        0       2        0       0
  89   9.95e-01 3.04e+00  -5.74878496e+32 -2.04137931e+00  1.02e+00       0s  1.00 1.00        0       2        0       0
  90   3.79e-03 3.45e+00  -5.74878496e+32 -2.44627943e+00  4.97e-02       0s  0.98 1.00        0       2        0       0
  91   1.31e-04 3.04e+00  -5.74878496e+32 -2.04382734e+00  1.58e-02       0s  1.00 1.00        0       2        0       0
  92   3.12e-10 3.04e+00  -5.74878496e+32 -2.04166195e+00  6.36e-08       0s  1.00 1.00        0       2        0       0
  93   3.01e-08 3.04e+00  -5.74878496e+32 -2.04137931e+00  1.04e-08       0s  1.00 1.00        0       2        0       0
  94   3.01e-08 3.04e+00  -5.74878496e+32 -2.04137931e+00  1.68e-08       0s  1.00 1.00        0       2        0       0
  95   3.24e-10 3.04e+00  -5.74878496e+32 -2.04137931e+00  3.45e-09       0s  0.50 1.00        0       2        0       0
  96   4.39e-05 3.04e+00  -5.74878496e+32 -2.04137931e+00  4.49e-05       0s  1.00 1.00        0       2        0       0
  97   3.30e-11 3.04e+00  -5.74878496e+32 -2.04137931e+00  4.12e-11       0s  1.00 1.00        0       2        0       0
  98   2.94e-12 3.04e+00  -5.74878496e+32 -2.04137931e+00  3.10e-11       0s  0.00 1.00        0       2        0       0
  99   2.94e-13 3.04e+00  -5.74878496e+32 -2.04137931e+00  3.01e-11       0s  0.00 1.00        0       2        0       0
 100   2.94e-13 3.04e+00  -5.74878496e+32 -2.04137931e+00  9.99e-14       0s  1.00 1.00        0       0        0       0
 101   2.94e-13 3.04e+00  -5.74878496e+32 -2.04137931e+00  1.66e-13       0s  1.00 1.00        0       2        0       0
 102   2.61e+15 4.77e+00  -5.74878496e+32 -2.04137931e-01  9.02e+17       0s  1.00 0.00        0       2        0       0
 103   1.29e+16 3.90e+00  -5.74878496e+32 -2.90041379e+00  1.94e+19       0s  1.00 0.85        0       2        0       0
 104   5.21e+15 3.26e+00  -5.74878496e+32 -1.55090323e+00  1.34e+19       0s  1.00 1.00        0       2        0       0
 105   9.37e+15 3.28e+00  -5.74878496e+32 -2.27506583e+00  1.55e+18       0s  1.00 1.00        0       2        0       0
 106   9.46e+15 3.53e+00  -5.74878496e+32 -2.53497647e+00  1.20e+17       0s  0.98 1.00        0       2        0       0
 107   1.82e+16 3.50e+00  -5.74878496e+32 -2.50102300e+00  1.37e+14       0s  0.99 1.00        0       2        0       0
 108   1.60e+16 4.72e+00  -5.74878496e+32 -2.50102300e-01  2.72e+16       0s  1.00 0.09        0       2        0       0
 109   1.17e+15 4.03e+00  -5.74878496e+32 -3.03211376e+00  3.76e+17       0s  1.00 1.00        0       2        0       0
 110   1.29e+16 3.99e+00  -5.74878496e+32 -8.96550543e-01  9.41e+17       0s  1.00 1.00        0       2        0       0
 111   2.39e+15 3.41e+00  -5.74878496e+32 -2.41419992e+00  3.99e+17       0s  1.00 1.00        0       2        0       0
 112   1.44e+16 3.19e+00  -5.74878496e+32 -2.19274223e+00  1.99e+16       0s  0.95 1.00        0       2        0       0
 113   1.76e+15 3.00e+00  -5.74878496e+32 -1.89445668e+00  8.50e+14       0s  1.00 1.00        0       2        0       0
 114   9.15e+11 3.15e+00  -5.74878496e+32 -2.14571740e+00  1.03e+13       0s  0.99 1.00        0       2        0       0
 115   1.72e+11 3.62e+00  -5.74878496e+32 -2.62349216e+00  1.24e+12       0s  1.00 1.00        0       2        0       0
 116   1.26e+10 3.46e+00  -5.74878496e+32 -2.46470378e+00  9.50e+09       0s  0.95 1.00        0       2        0       0
 117   4.60e+08 3.19e+00  -5.74878496e+32 -2.18831085e+00  2.65e+08       0s  1.00 1.00        0       2        0       0
 118   2.20e+05 3.29e+00  -5.74878496e+32 -2.28615594e+00  2.64e+06       0s  0.99 1.00        0       2        0       0
 119   2.13e+04 3.59e+00  -5.74878496e+32 -2.58972751e+00  2.49e+05       0s  1.00 1.00        0       2        0       0
 120   1.12e+03 3.48e+00  -5.74878496e+32 -2.48207953e+00  9.16e+02       0s  0.96 1.00        0       2        0       0
 121   4.06e+01 3.21e+00  -5.74878496e+32 -2.21295992e+00  2.30e+01       0s  1.00 1.00        0       2        0       0
 122   1.57e-02 3.29e+00  -5.74878496e+32 -2.29188664e+00  1.88e-01       0s  0.99 1.00        0       2        0       0
 123   5.03e-04 3.09e+00  -5.74878496e+32 -2.08714960e+00  1.13e-02       0s  0.98 1.00        0       2        0       0
 124   2.42e-07 3.04e+00  -5.74878496e+32 -2.04152602e+00  1.91e-05       0s  1.00 1.00        0       2        0       0
 125   6.62e-11 3.04e+00  -5.74878496e+32 -2.04137931e+00  4.18e-11       0s  1.00 1.00        0       2        0       0
 126   6.62e-11 3.04e+00  -5.74878496e+32 -2.04137931e+00  2.69e-11       0s  1.00 1.00        0       2        0       0
 Constructing starting basis...
    Number of free variables in starting basis:         1
    Number of singletons in starting basis:             0
    Number of other columns matched:                    0
    Number of other columns failed:                     1
    Number of columns dropped from guessed basis:       0
    Minimum singular value of crash basis:              2.00e+00
    Number of free variables nonbasic:                  2
    Unbounded primal ray with objective change:         -6.12e+00
    Number of free variables swapped for stability:     0
    Number of fixed variables basic:                    0
    Number of fixed variables swapped for stability:    0
Summary
    Runtime:                                            0.02s
    Status interior point solve:                        primal infeas
    Status crossover:                                   not run
Got Not Set
{'status': 0, 'message': 'Not Set'}
==================================================================== short test summary info =====================================================================
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bounds_equal_but_infeasible - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bounds_equal_but_infeasible2 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bounds_equal_no_presolve - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_zero_column_2 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_zero_row_3 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_zero_row_4 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_singleton_row_eq_1 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_singleton_row_ub_1 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_infeasible - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_infeasible_inequality_bounds - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_unbounded - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_unbounded_no_nontrivial_constraints_1 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_unbounded_no_nontrivial_constraints_2 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_remove_redundancy_infeasibility - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_enzo_example_c_with_unboundedness - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_enzo_example_c_with_infeasibility - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bug_8664 - KeyError: 0

Had to use Matlab, but hopefully this is right.
 IPM fails all for the same reason. Looks like res['status'] is 0 for these problems.

I have an issue open here to try to read the IPX statuses.  Right now I don't know how to.

Can't turn presolve on, though, because that still segfaults on at least one problem.

I'm still trying to figure MPS files out and the mpswriter build into HiGHS seems to do the wrong thing (generated integer constraints when there are none).  However, MPS files are the best way to communicate with their team and find out where the bugs actually are. If the MPS file is solved correctly by their application, then we know it's something in the wrapper.  If it fails on ours and theirs, then they have a test case they can fix.
With that said, I also get the segfault, but I'm having trouble putting together the MPS file just because I'm new at it.  I'll keep at it.  If you have anything that generates them from the problem descriptions we have (c, A, b, etc), I would be ecstatic.  I didn't find any ease to use tool like that when I googled around

Had to use Matlab, but hopefully this is right.

Great!  I'll give it a go

If you need to do this more and you have Matlab access, you could, try scipy.io.savemat and
https://www.mathworks.com/matlabcentral/fileexchange/19618-mps-format-exporting-tool
I did it manually, without scipy.io.savemat, but I used that MPS exporter.
A = [1, 0, 0, 0, 0; 0, 1, 0, 0, 0]
b = [2; -2];
L = [-inf; -inf; -inf; -1; -1];
U = [+inf; +inf; +inf; 1; 1];
Aeq = [];
beq = [];
cost = [0, 0, 0, 1, -1];
Contain = BuildMPS(A, b, Aeq, beq, cost, L, U, 'Unusual', 'MPSfilename', 'unusual.mps');

I don't have MATLAB on my home machine.  Does octave have a similar function?
I do get the following warning though:
loadLpFromFile: unusual.mps
22:24:31 [WARNING] BOUNDS section contains col X3 not in COLS section: ignored

Adding
    X3        COST      0

to columns section seemed to do it

They have an error on presolve with IPX, but do solve it with fun=-2 when presolve is off.  I'll have to dig in and see what the actual values they get are, it doesn't show me right off the bat

Yeah, it was giving me the right objective function value, but that function value does not follow from the solution vector it gave.

Issue reported here, appears to be a bug with HiGHS.

@mdhaber Got fixes from upstream, test_bug_8973 should now be passing on scikit-highs==0.0.29.

OK, I added back to test_bug_8973 a check that c @ x == -2 to make sure this doesn't come up again.

Another overhaul to the build system to try to deal with issue #8 , it's working for my local build and when I install into a virtual environment from pypi (both Linux).  I'll try on Windows later tonight.  If you get any errors, let me know and I can try to troubleshoot.

If you get any errors, let me know and I can try to troubleshoot.

Back to the clock_monotonic thing again. BTW, I'm only pulling master unless you tell me otherwise, right?

Back to the clock_monotonic thing again

On Windows?  Everything will be on master going forward.

On Windows?

Windows too. I'll post the most recent log in https://github.com/mckib2/HiGHS/issues/7

I don't expect Windows or Mac to work until I merge windows-experimental into master.  I'm working on that now.  Do things still work for you in WSL?

Do things still work for you in WSL?
Yes.

I haven't been getting the segfaults anymore, seems to have gone away with upstream changes.  I've added some MPS files for a couple of the failing unit tests to issue #287

Yeah, no more segfaults. That was easy!
Only three tests fail with presolve on by default: test_bug_6690, test_enzo_example_c_with_infeasibility, and test_bounds_equal_no_presolve. All of them fail only with IPM, and all of them fail with KeyError: 0.
In both test_enzo_example_c_with_infeasibility, and test_bounds_equal_no_presolve, presolve is explicitly disabled, and those examples pass with presolve on. It turns out nit=0, so if that is working correctly, that means that they are completed in presolve; there is only a problem when it gets to the main solver. Both of them are also infeasible. You wrote in https://github.com/ERGO-Code/HiGHS/issues/287 :

Using patches from pull request #300 successfully returns the model status!

Did those make it into master yet?
If not, then I think we really only have one bug left, test_bug_6690. This one is not infeasible, yet it's getting KeyError: 0.

Did those make it into master yet?

Not yet, I suspect it will make it in tomorrow.

If not, then I think we really only have one bug left, test_bug_6690. This one is not infeasible, yet it's getting KeyError: 0. I know that this problem has caused issues on some platforms with my sparse interior point solver, and it used to trigger a weird bug with the original simplex method. Might be one to pass along to the HiGHS team. How did you end up putting together those MPS files?

I've figured out how to make them by hand, so if it's a small enough problem I'll be able to make it.  KeyError: 0 just means that the status is UNSET, so it may or may not have resulted from missing IPX status.  I'll look at the bug more closely after that pull request is merged, I think they also fixed the handling of several other statuses.

Ok. If that test is still failing, I can make the MPS.
On Sun, Apr 5, 2020, 9:46 PM mckib2<EMAIL_ADDRESS>wrote:

Did those make it into master yet?
Not yet, I suspect it will make it in tomorrow.
If not, then I think we really only have one bug left, test_bug_6690.
This one is not infeasible, yet it's getting KeyError: 0. I know that
this problem has caused issues on some platforms with my sparse interior
point solver, and it used to trigger a weird bug with the original simplex
method. Might be one to pass along to the HiGHS team. How did you end up
putting together those MPS files?
I've figured out how to make them by hand, so if it's a small enough
problem I'll be able to make it. KeyError: 0 just means that the status
is UNSET, so it may or may not have resulted from missing IPX status. I'll
look at the bug more closely after that pull request is merged, I think
they also fixed the handling of several other statuses.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/scipy/scipy/pull/11759#issuecomment-609562774, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/ABSEEKYJJWQLHL6XM76CJQDRLFNCPANCNFSM4LWZIABQ
.

Fixes have been merged upstream and are now present in scikit-highs==0.4.0.  Results of unit tests reveal only two are failing:
========================================================== short test summary info ===========================================================
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_zero_row_4 - KeyError: 0
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_enzo_example_c_with_unboundedness - AssertionError:

test_zero_row_4 says that ipx doesn't run when presolve=False, but succeeds when presolve=True.  Is this expected behavior, i.e. should we turn presolve on for this unit test or is this a bug we should report to the HiGHS team?
test_enzo_example_c_with_unboundedness reports Infeasible when presolve=False, but reports unbounded when presolve=True.   Same question: is this a bug we should report to the HiGHS team or should we turn presolve on for this unit test?

Iteration limits are also now working, status code is 12, so could be decoded like:
statuses = {
    ...
    12 : ([whatever the scipy status code is], "Reached iteration limit"),
    ...
}

Added iteration limit reached status.
That's weird - those tests aren't failing in c3cc5aeee3520d43ba03f885f4affbcc35f24b93. Only the three mentioned above.
I'll try the latest version on my machine now.
We shouldn't turn presolve on to pass the tests. If it is off, it was intentional to leave it off because people submit issues when linprog doesn't solve simple problems - which tend to be because they're corner cases like these.  So we need to deal with them somehow. It would be great if they're willing to fix them upstream, but it depends on whether they want to spend time on these things. I'm not sure if their goal is just a high-performance solver or if they want a bulletproof high-performance solver : )

Hmm I just updated to the latest version and I'm only getting:
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bug_6690 - KeyError: 0

I've got a pull coming to your fork that integrates my HiGHS as a subtree, let's get to there and see what breaks.

@mdhaber  Is there a reason we wouldn't use -O3 optimization flag?  It takes a bit longer to compile, but passes all the same unit tests.  I've been using the cython default -O2 flag up to now

Sorry, I don't know!

@mdhaber  Made pull request #2 .  Let me know if something is not working, I'm going to test on Windows and Mac now.

OK, i'm testing too

BTW have you set up a SciPy development environment on Windows? The instructions are a bit buried because they're experimental. I've followed them to the letter and they've worked for me and at least one other person. Let me know if they work for you and maybe we'll bring them into the main contributor guide.

My Mac environment doesn't have Fortran compiler available...  So I'll have to rely on you to know if it works on Mac till/if I can get that sorted out

BTW have you set up a SciPy development environment on Windows? The instructions are a bit buried because they're experimental.

I haven't, but looks like I'll need to to verify this is working.  Time estimate was 3 hours, so I'll give it a go tomorrow

Whoops, that's a typo -- should be -std=c++14

@mckib2 let's see how this goes!

Well, at the least the AppVeyor error is one I've never seen before

OK. I can fix the PEP8 errors in linprog_highs.py.
Also note in Azure:
ImportError while importing test module 'C:\hostedtoolcache\windows\Python\3.7.6\x64\lib\site-packages\scipy\optimize\_highs\pyHiGHS\tests\test_simple_presolve.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
__init__.py:12: in <module>
    from pyHiGHS.highs_wrapper import highs_wrapper
E   ModuleNotFoundError: No module named 'pyHiGHS.highs_wrapper'

And the KeyError: 0 bug in test_bug_6690 is back.  I guess I should generate MPS for that one, too.

I'm starting this list to keep track of errors/warnings not fixed by removing tests and examples:

[ ] Travis CI ppx64le: scipy/optimize/_highs/src/util/HighsTimer.h:26:23: fatal error: x86intrin.h: No such file or directory

Is it weird to you that test_zero_row_4 and test_enzo_with_unboundedness are not failing again, and test_bug_6690 is back to failing?
Anyway, here are MPS files for all three: https://www.dropbox.com/sh/rdaekkdpvqr5isz/AADJ_UvzIN2Z5ZVHVCA9XL8_a?dl=0
I hope they are correct. To confirm, test_zero_row.mps should be infeasible, testEnzo.mps should be unbounded, and the solution to test6690.mps should be:
        desired_fun = -1.19099999999
        desired_x = np.array([0.3700, -0.9700, 0.3400, 0.4000, 1.1800,
                              0.5000, 0.4700, 0.0900, 0.3200, -0.7300])

We may need help for ppc64le support, it looks like it needs to be an upstream change to HiGHS to use the correct header for ppc64le instead of <x86intrin.h>, see here.
Some info here makes it seem like we need to choose between these three:

mmintrin.h (MMX)
xmmintrin.h (SSE)
emmintrin.h (SSE2)

x86intrin.h adds support for MMX, SSE and SSE2, and I don't which of the above does the "right thing."

Is it weird to you that test_zero_row_4 and test_enzo_with_unboundedness are not failing again, and test_bug_6690 is back to failing?

That is strange that different ones are failing, I'm not sure what changes when built with scipy and stand-alone.  I'll try out the MPS files.
Should I open another pull request to your fork? Is there a better way to test of with CI or is this the best way?

@rgommers Do you know who we might talk to about https://github.com/scipy/scipy/pull/11759#issuecomment-611852525

Should I open another pull request to your fork?

Yeah this is the best way I know. But if the PRs have experimental things, we should try to get help and ask them to check out the PRs before I merge them into my branch. We shouldn't go overboard iterating with CI.

Three new PEP8 issues appeared after my commit:
scipy/optimize/_highs/examples/call_highs_from_python.py:12: undefined name 'Highs_call'
scipy/optimize/_highs/check/pythontest.py:1: undefined name 'execfile'
scipy/optimize/_highs/check/pythontest.py:11: undefined name 'call_highs'

Can we remove some of the unnecessary stuff? I don't think we should distribute the original bindings, for example.

@rgommers Do you know who we might talk to about #11759 (comment)

I commented on the upstream issue, let's try to get the issue clear first.

We shouldn't go overboard iterating with CI.

I'm trying to get CI set up locally to see if I can debug some of this

@mdhaber  Using the latest highs_wrapper I am able to recreate results from CI build, only one failing test:
========================================================== short test summary info ===========================================================
FAILED scipy/optimize/tests/test_linprog.py::TestLinprogHiGHSIPM::test_bug_6690 - KeyError: 0
=========================================== 1 failed, 719 passed, 10 skipped, 2 xpassed in 12.19s ============================================

Using the MPS file you created, it appears their solver fails when presolve is on, but succeeds when presolve is off (simplex succeeds whether or not presolve is on/off).  I'll submit an issue upstream as it appears to be a HiGHS bug.

Three new PEP8 issues appeared after my commit:

I removed these files from my fork, should fix those issues

@mckib2 Close!
ImportError while importing test module '/scipy/build/testenv/lib/python3.6/site-packages/scipy/optimize/_highs/pyHiGHS/tests/test_simple_presolve.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
scipy/optimize/_highs/pyHiGHS/tests/test_simple_presolve.py:6: in <module>
    from pyHiGHS import highs_wrapper
E   ModuleNotFoundError: No module named 'pyHiGHS'

All errors so far in CI look like this:
ImportError while importing test module 'C:\Python37-x64\lib\site-packages\scipy\optimize\_highs\pyHiGHS\tests\test_simple_presolve.py'.

Looks like a defunct test script made it's way back in during a merge conflict resolution and I didn't catch it.

It looks like some of the Windows tests are timing out?  Not sure what to do about that.

It looks like some of the Windows tests are timing out?

Take a look in some of the other PRs that are failing. That seems to be happening here and there.

Failing on timeout for Windows Python36-32bit-full.  Almost gets through all tests without any unexpected failures (99%)
All other tests pass

What's the next steps for timeout failures?

Timeout is being investigated on #11704, I think.

Did something with CI build change?  We are now all passing

Did something with CI build change?

Yes, the 32-bit Windows failure was never our fault and people have been working on it.

@Kai-Striega @janvle @hakeemo @AtsushiSakai @andrewdelong I thought you might be interested in this. The documentation is not quite ready, but the functionality is there. Thought you might like to take a test drive and review. I've run the benchmarks and both of the new methods are usually faster, sometimes by quite a bit; they're also quite reliable. I hate to say it, but we should consider changing the default method - at least after a release or so to see if there are any issues we don't find.

When making the final tweaks to documentation and options, I noticed the following:

"Got Optimal" is printed to the console when the solve is successful, regardless of 'disp' option. ("Got infeasible", "Got Unbounded" print, too, I think.) Is that in the wrapper, or did I add that somewhere and forget?
I still can't seem to turn off the console output for IPM.
Simplex can crash Python when the iteration limit is reached. Example code below.
When IPM reaches iteration limit, it returns nit=0. I can fix that if you prefer, of course (nit=maxiter). Just let me know.
Is it OK to set both:

    options['ipm_iteration_limit'] = maxiter
    options['simplex_iteration_limit'] = maxiter

? I ask because when I let HiGHS choose the solver, I don't know which one to set.  It seems like it works, and setting both to None seems to leave it unlimited. Just checking.

What should I pass into the new options I'm adding (e.g. dual_feasibility_tolerance) when the user doesn't specify a value explicitly? Some of the defaults are in your documentation, but I couldn't find all of them.

Example that causes Python to crash:
np.random.seed(0)
c = np.random.rand(10)-0.5
A_ub = np.random.rand(5,10)
b_ub = np.random.rand(5)
options = {"maxiter": 3}
res = linprog(c, A_ub, b_ub, method = 'highs-simplex', options=options)
print(res)

"Got Optimal" is printed to the console when the solve is successful, regardless of 'disp' option. ("Got infeasible", "Got Unbounded" print, too, I think.) Is that in the wrapper, or did I add that somewhere and forget?

That's an old debug statement I made in the wrapper, I'll remove it

I still can't seem to turn off the console output for IPM.

I think this might be a "feature," I'll create an issue asking for silently running the IPM solver.  This looks like it's been discussed, but I don't think it exists yet.

Simplex can crash Python when the iteration limit is reached. Example code below.

I think I know what's happening, but I'll take a look to make sure.  Each iteration should produce a feasible solution, so I try to pass back the current solution if iteration limit is reached.  Perhaps this is the wrong thing to do.

When IPM reaches iteration limit, it returns nit=0. I can fix that if you prefer, of course (nit=maxiter). Just let me know.

This might be a bug -- IPM iteration limit was added recently.  Do you have an example that reproduces it?

Is it OK to set both: ...

Yes, I don't see a reason that wouldn't work.

What should I pass into the new options I'm adding (e.g. dual_feasibility_tolerance) when the user doesn't specify a value explicitly? Some of the defaults are in your documentation, but I couldn't find all of them.

According to docs the default: 1e-07.  Passing None should use the default as well.

Great. My favorite type of error to debug: DOCUMENTATION.
Note: could you check your definitions of con and slack? They should be b_eq - A_eq@x and b_ub - A_ub@x, respectively, to match the rest of the methods. It looks like con is a scalar, and slack has too many elements.

con is simply forwarding sum_primal_infeasibilities, so yes this is just a scalar and not the correct con value that we expect.
slack is returning rhs - row_value, so it has num_rows entries.  To translate to linprog, is it as simple as splitting up rhs - row_value into con and slack?  Like this:
...
`'slack': res.get('slack')[rows of rhs that correspond to A_ub]`,
`'con': res.get('slack')[rows of rhs that correspond to A_eq]`,
...

Great. My favorite type of error to debug: DOCUMENTATION.

Are these simply documentation errors?  I didn't change anything drastic to make anything fail, I don't think

Are these simply documentation errors?

Yes, it's my fault. We'll see if the change fixes it.

To translate to linprog, is it as simple as splitting up...

Yes, that sounds right. Should add two lines to an existing test:
_assert_array_equal(res.slack, b_ub - A_ub @ res.x)
_assert_array_equal(res.con, b_eq - A_eq @ res.x)

Are these simply documentation errors?

I think it's the lists of option values in the docstring of linprog_highs that I added. I can never remember exactly how sphinx wants things formatted.

FYI:

highs-323: upstream fixes for silently running IPM; waiting to merge into master
highs-324: upstream fixes for robustly retrieving existing solution in case of timeout or error

Please submit or reiterate anything on the wishlist for HiGHS

It can read MPS files - can it write them? Otherwise, I'm sure we'll think of things, but I don't have a list right now.

It can read MPS files - can it write them? Otherwise, I'm sure we'll think of things, but I don't have a list right now. Don't want to overwhelm them!

Yes and no.  It does have a writer but the wrapper I have for it  likes to add in integer constraints on variables that shouldn't have integer constraints.
I think a tool for converting linprog problems statements (A_ub, A_eq, b_ub, etc.) into MPS file format would be very useful.  It would also be useful to allow an MPS file to be passed straight into HiGHS.
I was thinking of opening another pull request for this after this one was merged, but I'm happy to look at it again and see if there is an easy fix.  What would the interface look like?

What would the interface look like?

I would say the same as the first arguments of linprog:
mps_writer(c, A_ub, b_ub, A_eq, b_eq, bounds)
and possibly an index of which decision variables are integer.
But I would be happy with anything, as long as it accepts arrays of some sort. (For example, maybe they'd prefer to have to have left and right limits on A@x rather than separate equality and inequality constraint matrices.) We can do any minor manipulations ourselves.

@rgommers Clearly this is a bit much to review line by line : ) How do you think this should be reviewed?

For speed and accuracy, the benchmarks show that this is a big improvement, so I don't think we need line by line checking for correctness or even efficiency. Whatever it is, it's much faster than what we currently have.
For style, the only pure Python file is _linprog_highs.py and it's pretty short. Perhaps @mckib2 can point out the files he wrote that someone might want to take a look at.
Here's a direct link to the revised linprog documentation.
Are we happy with the way this was integrated into our build system?

For style, the only pure Python file is _linprog_highs.py and it's pretty short. Perhaps @mckib2 can point out the files he wrote that someone might want to take a look at.

The only interesting files I wrote are:

highs_wrapper.pyx
mpswriter.pyx

highs_wrapper.pyx is a little beefier than mpswriter.pyx, but both are manageable.

@EverLookNeverSee Since you just wrote the linprog tutorial, would you be interested in trying out these new methods?

Yeah these kind of things are a bit of a pain to review. I would suggest to focus on the files that @mckib2 pointed out, on test completeness and coverage (actually hypothesis may be helpful here), on documentation, and on obvious issues in the code.
Given the previous concerns on portability, I think it would also be healthy to try a non-standard platform. Having the new ARM64 CI merged will help here (going to look at that now).

@EverLookNeverSee Since you just wrote the linprog tutorial, would you be interested in trying out these new methods?

You meant @AtsushiSakai.

@mdhaber Yes. If there is something I can do, I would like to work for it.

@AtsushiSakai Would you check out this PR and test linprog with method='highs-simplex', 'highs-ipm', and 'highs' with simple problems (e.g. the one in the tutorial you wrote) but with various options (both general linprog and method-specific) mentioned in the documentation? Do the new methods respond as you would expect? Does the output in the OptimizeResult make sense?

@rgommers

focus... on test completeness and coverage

This is just a new method, and it passes all of linprog's current tests. Do you mean that as part of the PR we might add new tests that all of the other linprog methods must pass?

This is just a new method, and it passes all of linprog's current tests. Do you mean that as part of the PR we might add new tests that all of the other linprog methods must pass and fix any of those failures we might find?

No. I thought there was some new capabilities; on re-reading I see This adds just the linear programming functionality; MIP functionality will come in a separate PR. in the PR description. So yes, if current test coverage is fine, that settles that I think - not much to do. Then it's just performance, portability, and reviewing the key bits of code.

Thanks @rgommers. According to the benchmarks, performance is much better. Do you have recommendations for a Cython reviewer?
We'll re-run CI when ARM64 is added. Will that address portability, or who else would you recommend to review for that?
@Kai-Striega would you be able to review some of the key bits of code? _linprog_highs.py is the only interesting Python code, and highs_wrapper.pyx and mpswriter.pyx are the interesting Cython code (if you're able).

@mdhaber Hi. I tried this new solver out for a day. First of all, this new solver is so fast. In my personal benchmark, "highs-simplex" is about 3 times faster than previous methods. I believe a lot of users including me will welcome this new solver. Thank you so much for you guys great work!!.
I have some minor feedbacks and questions as below:
1
Currently we  have 5 methods in linprog. I think some users will be wondering which method to use for their problems. So, I think it would be better there is a tutorial or a table to explain pros and cons of each method.
2
When I use "highs-ipm", some outputs are shown in stdout by default. But other methods don't output in stdout by default. I think "hights-ipm" should suppress stdout by default for consistency.
3
I tried to solve a simple LP with all methods. When I compared each OptimizeResult, I noticed that, the slack variables of  non-highs methods is

slack: array([ 6.52746301e-10, -2.26889441e-09])

But, highs one is

slack: [0.0, 0.0, 0.0, 0.0]

The type and shape of slack variable looks different. If it is possible, all result["slack"] should be same.
4
When should we set presolve=False in highs solver? I think a description about it would be better to be added in the API doc.
5
When I changed message_level option, it looks like that nothing changed.  What was going to change?
6
When I input an invalid integer for an option, for example "simplex_dual_edge_weight_strategy": -1, nothing happened. Invalid input validation and error message would be better to be added.
7
The both API doc has some minor issues:

the default value of message_level is 0, but it shows 1
The doc says solver is one of option, but when I input it as an option, an exception was thrown:

scipy.optimize.optimize.OptimizeWarning: Unknown solver options: solver

Thanks @AtsushiSakai that is exactly what I was looking for. I'll address these comments individually soon!

Hi @AtsushiSakai , thanks for the great feedback!  I can address some of these.

The type and shape of slack variable looks different. If it is possible, all result["slack"] should be same.

Yes, we agree!  This is a difference due to how HiGHS represents constraints internally.  There is discussion and fix in https://github.com/mdhaber/scipy/pull/10

5
When I changed message_level option, it looks like that nothing changed. What was going to change?

The disp option is part of the existing linprog API and is an on/off switch for messages.  message_level is an option specific to the HiGHS solver and allows for a little more granularity in message output, but is only used if disp=True.  By default disp=False, so setting disp=True should be enough to see changes to message_level.

6
When I input an invalid integer for an option, for example "simplex_dual_edge_weight_strategy": -1, nothing happened. Invalid input validation and error message would be better to be added.

When run with disp=True and message_level=1 the following warning is printed:
WARNING: checkOptionValue: Value -1 for option "simplex_dual_edge_weight_strategy" is below lower bound of 0

The default is used when an invalid value is found.  However, if disp=False then no warning message is printed to the screen.

@mckib2 OK. I see. I think just improving API doc is enough for 5,6. Thank you for great work!!.

So I have run the benchmarks and here are the results...
>>> python run.py run --bench optimize_linprog
...
[ 58.33%] ··· optimize_linprog.KleeMinty.track_klee_minty            6/15 failed
[ 58.33%] ··· ======================================= ============ ========================
                               method                  dimensions
              --------------------------------------- ------------ ------------------------
                ('interior-point', {'sparse': True})       3        7.866788109822665e-12
                ('interior-point', {'sparse': True})       6        1.9560330896638334e-08
                ('interior-point', {'sparse': True})       9        2.9340267181396486e-11
               ('interior-point', {'sparse': False})       3        7.870312401792034e-12
               ('interior-point', {'sparse': False})       6        1.9560328451916575e-08
               ('interior-point', {'sparse': False})       9        2.934837341308594e-11
                      ('revised simplex', {})              3                 0.0
                      ('revised simplex', {})              6                 0.0
                      ('revised simplex', {})              9                 0.0
                         ('highs-ipm', {})                 3                failed
                         ('highs-ipm', {})                 6                failed
                         ('highs-ipm', {})                 9                failed
                       ('highs-simplex', {})               3                failed
                       ('highs-simplex', {})               6                failed
                       ('highs-simplex', {})               9                failed
              ======================================= ============ ========================

see the full results: benchmark_out.txt
It looks like there are some problems running the benchmarks (at least on my system). I'm working from my laptop so these take a while to run but I can dig into them over the weekend to see if I can find a cause.

I've run the benchmarks and both of the new methods are usually faster, sometimes by quite a bit; they're also quite reliable. I hate to say it, but we should consider changing the default method - at least after a release or so to see if there are any issues we don't find. -- @mdhaber (comment)

Should we consider a sixth option 'auto' that allows us to choose the solver for the user?
That way we can accommodate future changes without explicitly changing the default solver. Once (if) they are implemented this would also allow us to combine solvers e.g. find a basis using interior-point then an exact solution using revised-simplex or choosing a suitable solver based off some problem heuristics.

So I have run the benchmarks and here are the results

My understanding is that by default asv executes the benchmarks of the active branch but  - astonishingly - builds head of the master branch. You need to specify which branch you want to benchmark on.

@AtsushiSakai

2
When I use "highs-ipm", some outputs are shown in stdout by default. But other methods don't output in stdout by default. I think "hights-ipm" should suppress stdout by default for consistency.

This is also addressed in https://github.com/mdhaber/scipy/pull/10, we are waiting for an upstream bug fix (in the works) to merge

asv executes the benchmarks of the active branch but -  but - astonishingly - builds head of the master branch.

Oh. I didn't realise it does that. Looking at it now that's what we're telling it to do. Changing "branches": ["master"]  ⇾ "branches": ["HEAD"] in  asv.conf.json builds the head of the current branch.
Perhaps that should be changed - but that can be another PR. Either way the benchmarks run fine now. Here are the results if you're interested.

That's what method='highs' does - it chooses between the two HiGHS solvers automatically.

I wasn't aware of method='highs'.  I can't see it in the documentation? The benefit of method='auto' has over method='highs' is that, in my opinion, it allows us to be more flexible regarding future changes, without having to (potentially) change the default solver again. At this point I'm only thinking out loud - this could turn out to be a terrible idea.

I'm not sure when we would choose to call our interior point or revised simplex solver... Maybe if the problem is dense?

Looking at my benchmarks both rs and interior-point are slower even for dense problems. Maybe for very dense, very large problems? I suspect in actual use cases, those will be rare enough that programming special cases won't be worth the effort... I guess that means we shouldn't?

Fixes in the works for the following CI failures:

[ ] ADS pipelines Run 32-bit Ubuntu Docker Build / Tests (Python 3.6) https://github.com/mckib2/HiGHS/issues/32
[ ] Travis CI AMD 64 Xenial Python 3.6 https://github.com/mckib2/HiGHS/issues/33

Let me know if anyone sees anything else, those listed above seem like simple-enough fixes

Those are all I see. I can fix the PEP8 issue when I address @AtsushiSakai's comments.

@mckib2 I was writing a test for slack and con and noticed that the logic in _linprog_highs.py doesn't work with method='highs'.
           'nit': (res.get('simplex_nit', 0) if solver == 'simplex'
                   else res.get('ipm_nit', 0))

How about:
           'nit': res.get('simplex_nit', 0) or res.get('ipm_nit', 0)

?

@mdhaber That looks reasonable to me.
In principle, should crossover iterators be added to ipm iterations?  I.e.,
'nit': res.get('simplex_nit', 0) or (res.get('ipm_nit', 0) + res.get('crossover_nit', 0))

All unit tests are passing, one Travis CI job timed out after running test suite

In principle, should crossover iterations be added to ipm iterations? I.e.,

I'm not sure. Crossover is relatively inexpensive, no? If that's the case, no, because the problem is solved after the number of IP iterations, and crossover is really just postprocessing.
Is there an option for turning crossover off?

Is there an option for turning crossover off?

I do not see any documented way to turn it off and doing a quick grep I can't find any obvious flags they are using internally.  So let's assume there is currently no way.  Can you add that documentation?  I'll ask upstream about this

Can you add that documentation

That it doesn't include crossover iterations? Sure.
I assume that maxiter does not control crossover iterations, either?

table to explain pros and cons of each method.

The "Notes" in the linprog documentation is supposed to address this. I actually even wrote "For new code, we recommend explicitly choosing one of these methods." After we give this a release or two to iron out any bugs, I think highs (which automatically chooses between highs-simplex and highs-ipm ) will become the default method, and there might not be any reason to choose the old methods. I don't think we will be able to advise on whether to choose IPM or simplex; that is problem-dependent.

When I use "highs-ipm", some outputs are shown in stdout by default. But other methods don't output in stdout by default. I think "hights-ipm" should suppress stdout by default for consistency.

I think that's fixed. But @mckib2 now I'm not seeing output out of either method ever, even with options={'disp': True}. Can you confirm?

3 re: incorrect slack variables

This is fixed.

When should we set presolve=False in highs solver? I think a description about it would be better to be added in the API doc.

OK, I adjusted the notes about presolve in the highs-specific documentation:
        Presolve attempts to  identify trivial infeasibilities,
        identify trivial unboundedness, and simplify the problem before
        sending the problem to the main solver. It is generally recommended
        to keep the default setting ``True``; set to ``False`` if presolve is
        to be disabled.

When I changed message_level option, it looks like that nothing changed. What was going to change?

I clarified the message_level documentation.
        Default is 1, but note:
        this option is ignored unless option ``disp`` is ``True``.

When I input an invalid integer for an option, for example "simplex_dual_edge_weight_strategy": -1, nothing happened. Invalid input validation and error message would be better to be added.

I added some warning messages for these sorts of things.

The both API doc has some minor issues:
the default value of message_level is 0, but it shows 1

It should say 1, but now I've clarified that it is ignored unless disp is True.

The doc says solver is one of option, but when I input it as an option, an exception was thrown

They also say lp is an option, but it's not really. This is a deficiency in the way all of the method-specific documentation pages are generated. It affects all of the linprog methods and I don't think we can fix it here.

@mckib2 A few issues I've noticed:

The default values of several HiGHS options are not specified. I'm sorry if you already answered about these, but if you did, I forget. Can you remind me?
Even with disp set to True, I can't get any output from the HiGHS solvers.
I set simplex_update_limit to 0 and there is no problem. Seems like that should fail?
simplex_strategy 4 fails, at least on this problem:

import numpy as np
from numpy.testing import assert_, assert_allclose
from scipy import optimize

np.random.seed(0)
m_eq, m_ub, n = 10, 20, 50
c = np.random.rand(n)-0.5
A_ub = np.random.rand(m_ub, n)-0.5
b_ub = np.random.rand(m_ub)-0.5
A_eq = np.random.rand(m_eq, n)-0.5
b_eq = np.random.rand(m_eq)-0.5
lb = -np.random.rand(n)
ub = np.random.rand(n)
lb[lb<-np.random.rand()] = -np.inf
ub[ub>np.random.rand()] = np.inf
bounds = np.vstack((lb, ub)).T
options={"disp": True, 'message_level': 1, "simplex_crash_strategy":0,
         "simplex_dual_edge_weight_strategy":0,
         "simplex_primal_edge_weight_strategy":0, "simplex_strategy":4,
         "simplex_update_limit": 1}
res = optimize.linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq,
                       bounds=bounds, method='highs', options=options)
assert_(res.success)
assert_(res.nit)
assert_(not res.status)
assert_(res.message == "Optimization terminated successfully.")
assert_allclose(c @ res.x, res.fun)
assert_allclose(b_eq - A_eq @ res.x, res.con, atol = 1e-12)
assert_allclose(b_ub - A_ub @ res.x, res.slack, atol = 1e-12)
print(res.nit)

Take a look at ci/circleci: build_docs artific Details and browse to linprog documentation. Is it rendering funny for you?

It's not restricted to this PR, but I'm wondering if it's just my computer?

@mdhaber I will check the latest one.

........................................................................ [ 97%]
........................................................................ [ 97%]
........................................................................ [ 97%]
........................................................................ [ 98%]
........................................................................ [ 98%]
........................................................................ [ 98%]
........................................................................ [ 98%]
........................................................................ [ 99%]
........................................................................ [ 99%]
........................................................................ [ 99%]
.....................................................................
The job exceeded the maximum time limit for jobs, and has been terminated.

Great.

Just ignore it for now - if it happens more often we'll have to go back and cut testing of one or more submodules.

Just ignore it for now - if it happens more often we'll have to go back and cut testing of one or more submodules.

annoying. Most of the time in that run was spent in the build. As a point of principle though I would like to amend the CI script to print out the 10 slowest test times as a matter of course to make sure we're not introducing 10 minute tests.

@rgommers could it do more verbose output so what we can check the tests that ran before it times out?

As a point of principle though I would like to amend the CI script to print out the 10 slowest test times as a matter of course to make sure we're not introducing 10 minute tests.

that sounds like a good idea

@rgommers could it do more verbose output so what we can check the tests that ran before it times out? (And is this a problem that a little money can solve? Is 50 minutes a free tier limit?)

I'd rather not make the output more verbose by default. Just ignore the timeouts, really not needed to verify that a particular test ran.
And yes 50 min is the free tier limit. Money also requires admin, rather speed things up a little .....

This is akin to:
python runtests.py -- --durations=5

I'd rather not make the output more verbose by default. Just ignore the timeouts, really not needed to verify that a particular test ran.

We wanted to see whether this PR passed all tests on ARM to check portability.

Take a look at ci/circleci: build_docs artific Details and browse to linprog documentation. Is it rendering funny for you?

I see the same thing, haven't tried building locally though.
@mdhaber I'll have a chance to take a look at your comments later today

@mdhaber  see https://github.com/mdhaber/scipy/pull/14
With a lot of these, the only real answer is "sorry, no documentation."  So I'll start putting some queries in and answering to the best of my understanding about what I see in the code.

The default values of several HiGHS options are not specified. I'm sorry if you already answered about these, but if you did, I forget. Can you remind me?

I think every time I've pointed you to here.  I'll go ahead and put them in the docstring and if you find any that are missing, let me know and I can dig them up

Even with disp set to True, I can't get any output from the HiGHS solvers.

Did you change the message_level?  Default now is off, so you might need to use message_level=MESSAGE_LEVEL_DETAILED or something to that effect.

I set simplex_update_limit to 0 and there is no problem. Seems like that should fail? (Unless your code or HiGHS is ignoring the invalid value - but I can't tell, because I can't see output)

Here's what I think is happening: update limit is counting how many until a basis reset, not how many updates are allowed.  So if you set a low number, all you're doing is calculating basis inverses explicitly.  Documentation is sparse, but that's what it looks like from the code

We should document which options are useful for which methods, e.g. as you confirmed before, the simplex_... options are ignored for ipm. How about dual_feasibility_tolerance, dual_objective_value_upper_bound, and primal_feasibility_tolerance?

For IPX,

dual_feasibility_tolerance is interpreted as ipm_optimality_tol
dual_objective_value_upper_bound looks like it is ignored by IPX
primal_feasibility_tolerance is interpreted as ipm_feasibility_tol

Does HiGHS provide any documentation for their option levels, e.g. SIMPLEX_CRASH_STRATEGY_LTSF_PRI? We probably should ask for them.

There is no documentation for these.  I'd say that if you are wanting to use a different crash strategy you probably know from the name what is going to happen.  For the rest of us, the default is the way to go.

simplex_strategy 4 fails, at least on this problem ...

I'll check and see if HiGHS breaks too or if it's a bug in the wrapper.

@mdhaber @mckib2 Hi. I checked the latest code and it looks like all issues I found were fixed. Thank you. If I found other any issues I will let you guys know.

Is it rendering funny for you?

I think that due to raw string of docstring.  If I changed ":" to "\:", it looks better:
Before

After

@mdhaber see mdhaber#14
With a lot of these, the only real answer is "sorry, no documentation." So I'll start putting some queries in and answering to the best of my understanding about what I see in the code.

The default values of several HiGHS options are not specified. I'm sorry if you already answered about these, but if you did, I forget. Can you remind me?

I think every time I've pointed you to here. I'll go ahead and put them in the docstring and if you find any that are missing, let me know and I can dig them up

Even with disp set to True, I can't get any output from the HiGHS solvers.

Did you change the message_level? Default now is off, so you might need to use message_level=MESSAGE_LEVEL_DETAILED or something to that effect.

I set simplex_update_limit to 0 and there is no problem. Seems like that should fail? (Unless your code or HiGHS is ignoring the invalid value - but I can't tell, because I can't see output)

Here's what I think is happening: update limit is counting how many until a basis reset, not how many updates are allowed. So if you set a low number, all you're doing is calculating basis inverses explicitly. Documentation is sparse, but that's what it looks like from the code and the probable meaning of UPDATE.

We should document which options are useful for which methods, e.g. as you confirmed before, the simplex_... options are ignored for ipm. How about dual_feasibility_tolerance, dual_objective_value_upper_bound, and primal_feasibility_tolerance?

I've updated
https://www.maths.ed.ac.uk/hall/HiGHS/HighsOptions.html
Setting update limit to 0 (or 1) means that HiGHS re-factorizes every basis matrix rather than updating the invertible representation [The inverse is never computed explicitly in HiGHS - for a host of reasons.] The code runs fine, but it'll be a lot slower on large problems!
dual_objective_value_upper_bound is used in only the dual simplex solver, and causes it to bail out when the objective (in Phase 2) reaches this limit. I's used to save time when the LP is a node from a MIP solver.
The simplex crash is off by default because it's not so useful with the dual simplex method and there are some numerical issues with it on some large, hard test problems. Making the most of crashing is work on the stack.
Fundamentally, you're exploring options - the advanced ones, and a few others - that are only there for development. Unfortunately we don't have a "system" where there are options available to us to play with that aren't available to users.

Thanks @jajhall!

Fixes coming in https://github.com/mdhaber/scipy/pull/15:

Travic CI exploded, but I think the fix is to add a missing C++11/14 flag to the new constants extension
A few upstream commits for suppressing debug outputs
Fix a failure case found by hypothesis (ModelError raised before highs is run)

Accidental merge. Thought I was in another tab.
This PR was getting ridiculously long anyway.