Skip to content

Commit fcb37be

Browse files
committed
Merge branch 'master' into rs/highs
2 parents 6bce768 + 2c4d42c commit fcb37be

File tree

8 files changed

+131
-51
lines changed

8 files changed

+131
-51
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Some of the main features of MIP are:
4141
different solvers are handled by Python-MIP and you write only one
4242
solver independent code;
4343

44-
* written in modern [typed](https://docs.python.org/3/library/typing.html) Python 3 (requires Python 3.6 or newer).
44+
* written in modern [typed](https://docs.python.org/3/library/typing.html) Python 3 (requires Python 3.8 or newer).
4545

4646
## Examples
4747

@@ -63,7 +63,7 @@ Questions, suggestions and feature request can be posted at [Discussions](https:
6363

6464
## Build status
6565

66-
[![Github Actions Status](https://github.com/coin-or/python-mip/workflows/CI/badge.svg?branch=master)](https://github.com/coin-or/python-mip/actions)
66+
[![CI](https://github.com/coin-or/python-mip/actions/workflows/github-ci.yml/badge.svg)](https://github.com/coin-or/python-mip/actions/workflows/github-ci.yml)
6767
[![Current version](https://badge.fury.io/gh/coin-or%2Fpython-mip.svg)](https://github.com/coin-or/python-mip/releases)
6868
[![Current total of lines](https://tokei.rs/b1/github/coin-or/python-mip?category=lines)](https://github.com/coin-or/python-mip)
6969
[![License](https://img.shields.io/badge/license-EPL-blue.svg)](https://github.com/coin-or/python-mip/blob/master/LICENSE)

examples/plant_location.py

+20-39
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@
1111

1212
import sys
1313

14-
# Workaround for issues with python not being installed as a framework on mac
15-
# by using a different backend.
16-
if sys.platform == "darwin": # OS X
17-
import matplotlib as mpl
18-
mpl.use('Agg')
19-
del mpl
20-
21-
import matplotlib.pyplot as plt
2214
from math import sqrt, log
2315
from itertools import product
2416
from mip import Model, xsum, minimize, OptimizationStatus
@@ -36,28 +28,26 @@
3628
C = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
3729

3830
# position of clients
39-
pc = {1: (94, 10), 2: (57, 26), 3: (74, 44), 4: (27, 51), 5: (78, 30), 6: (23, 30),
40-
7: (20, 72), 8: (3, 27), 9: (5, 39), 10: (51, 1)}
31+
pc = {
32+
1: (94, 10),
33+
2: (57, 26),
34+
3: (74, 44),
35+
4: (27, 51),
36+
5: (78, 30),
37+
6: (23, 30),
38+
7: (20, 72),
39+
8: (3, 27),
40+
9: (5, 39),
41+
10: (51, 1),
42+
}
4143

4244
# demands
4345
d = {1: 302, 2: 273, 3: 275, 4: 266, 5: 287, 6: 296, 7: 297, 8: 310, 9: 302, 10: 309}
4446

45-
# plotting possible plant locations
46-
for i, p in pf.items():
47-
plt.scatter((p[0]), (p[1]), marker="^", color="purple", s=50)
48-
plt.text((p[0]), (p[1]), "$f_%d$" % i)
49-
50-
# plotting location of clients
51-
for i, p in pc.items():
52-
plt.scatter((p[0]), (p[1]), marker="o", color="black", s=15)
53-
plt.text((p[0]), (p[1]), "$c_{%d}$" % i)
54-
55-
plt.text((20), (78), "Region 1")
56-
plt.text((70), (78), "Region 2")
57-
plt.plot((50, 50), (0, 80))
58-
59-
dist = {(f, c): round(sqrt((pf[f][0] - pc[c][0]) ** 2 + (pf[f][1] - pc[c][1]) ** 2), 1)
60-
for (f, c) in product(F, C) }
47+
dist = {
48+
(f, c): round(sqrt((pf[f][0] - pc[c][0]) ** 2 + (pf[f][1] - pc[c][1]) ** 2), 1)
49+
for (f, c) in product(F, C)
50+
}
6151

6252
m = Model()
6353

@@ -82,7 +72,7 @@
8272
D = 6 # nr. of discretization points, increase for more precision
8373
v = [c[f] * (v / (D - 1)) for v in range(D)] # points
8474
# non-linear function values for points in v
85-
vn = [0 if k == 0 else 1520 * log(v[k]) for k in range(D)]
75+
vn = [0 if k == 0 else 1520 * log(v[k]) for k in range(D)]
8676
# w variables
8777
w = [m.add_var() for v in range(D)]
8878
m += xsum(w) == 1 # convexification
@@ -98,30 +88,21 @@
9888

9989
# objective function
10090
m.objective = minimize(
101-
xsum(dist[i, j] * x[i, j] for (i, j) in product(F, C)) + xsum(y[i] for i in F) )
91+
xsum(dist[i, j] * x[i, j] for (i, j) in product(F, C)) + xsum(y[i] for i in F)
92+
)
10293

10394
m.optimize()
10495

105-
plt.savefig("location.pdf")
106-
10796
if m.num_solutions:
10897
print("Solution with cost {} found.".format(m.objective_value))
10998
print("Facilities capacities: {} ".format([z[f].x for f in F]))
11099
print("Facilities cost: {}".format([y[f].x for f in F]))
111100

112-
# plotting allocations
113-
for (i, j) in [(i, j) for (i, j) in product(F, C) if x[(i, j)].x >= 1e-6]:
114-
plt.plot(
115-
(pf[i][0], pc[j][0]), (pf[i][1], pc[j][1]), linestyle="--", color="darkgray"
116-
)
117-
118-
plt.savefig("location-sol.pdf")
119-
120101
# sanity checks
121102
opt = 99733.94905406
122103
if m.status == OptimizationStatus.OPTIMAL:
123104
assert abs(m.objective_value - opt) <= 0.01
124105
elif m.status == OptimizationStatus.FEASIBLE:
125106
assert m.objective_value >= opt - 0.01
126107
else:
127-
assert m.status not in [OptimizationStatus.INFEASIBLE, OptimizationStatus.UNBOUNDED]
108+
assert m.status not in [OptimizationStatus.INFEASIBLE, OptimizationStatus.UNBOUNDED]

mip/cbc.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Dict, List, Tuple, Optional, Union
55
from sys import platform, maxsize
66
from os.path import dirname, isfile, exists
7+
from platform import machine as platform_machine
78
import os
89
import multiprocessing as multip
910
import numbers
@@ -97,7 +98,9 @@
9798
elif platform.lower().startswith("darwin") or platform.lower().startswith(
9899
"macos"
99100
):
100-
if os_is_64_bit:
101+
if platform_machine().lower().startswith("arm64"):
102+
libfile = os.path.join(pathlib, "cbc-c-darwin-arm64.dylib")
103+
elif os_is_64_bit:
101104
libfile = os.path.join(pathlib, "cbc-c-darwin-x86-64.dylib")
102105
if not libfile:
103106
raise NotImplementedError("You operating system/platform is not supported")
@@ -1405,20 +1408,23 @@ def var_get_column(self, var: "Var") -> Column:
14051408

14061409
def add_constr(self, lin_expr: LinExpr, name: str = ""):
14071410
# collecting linear expression data
1408-
numnz = len(lin_expr.expr)
1411+
1412+
# In case of empty linear expression add dummy row
1413+
# by setting first index of row explicitly with 0
1414+
numnz = len(lin_expr.expr) or 1
14091415

14101416
if numnz > self.iidx_space:
14111417
self.iidx_space = max(numnz, self.iidx_space * 2)
14121418
self.iidx = ffi.new("int[%d]" % self.iidx_space)
14131419
self.dvec = ffi.new("double[%d]" % self.iidx_space)
14141420

14151421
# cind = self.iidx
1416-
self.iidx = [var.idx for var in lin_expr.expr.keys()]
1422+
self.iidx = [var.idx for var in lin_expr.expr.keys()] or [0]
14171423

14181424
# cind = ffi.new("int[]", [var.idx for var in lin_expr.expr.keys()])
14191425
# cval = ffi.new("double[]", [coef for coef in lin_expr.expr.values()])
14201426
# cval = self.dvec
1421-
self.dvec = [coef for coef in lin_expr.expr.values()]
1427+
self.dvec = [coef for coef in lin_expr.expr.values()] or [0]
14221428

14231429
# constraint sense and rhs
14241430
sense = lin_expr.sense.encode("utf-8")

mip/constants.py

+5
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ class OptimizationStatus(Enum):
134134
CUTOFF = 7
135135
"""No feasible solution exists for the current cutoff"""
136136

137+
INF_OR_UNBD = 8
138+
"""Special state for gurobi solver. In some cases gurobi could not
139+
determine if the problem is infeasible or unbounded due to application
140+
of dual reductions (when active) during presolve."""
141+
137142
OTHER = 10000
138143

139144

mip/gurobi.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,14 @@ def callback(
776776
if status == 3: # INFEASIBLE
777777
return OptimizationStatus.INFEASIBLE
778778
if status == 4: # INF_OR_UNBD
779-
return OptimizationStatus.UNBOUNDED
779+
# Special case by gurobi, where an additional run has to be made
780+
# to determine infeasibility or unbounded problem
781+
# For this run dual reductions must be disabled
782+
# See gurobi support article online - How do I resolve the error "Model is infeasible or unbounded"?
783+
# self.set_int_param("DualReductions", 0)
784+
# GRBoptimize(self._model)
785+
# return OptimizationStatus.INFEASIBLE if self.get_int_attr("Status") == 3 else OptimizationStatus.UNBOUNDED
786+
return OptimizationStatus.INF_OR_UNBD
780787
if status == 5: # UNBOUNDED
781788
return OptimizationStatus.UNBOUNDED
782789
if status == 6: # CUTOFF
5.77 MB
Binary file not shown.

mip/model.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,7 @@ def add_constr(
338338
raise mip.InvalidLinExpr(
339339
"A boolean (true/false) cannot be used as a constraint."
340340
)
341-
# TODO: some tests use empty linear constraints, which ideally should not happen
342-
# if len(lin_expr) == 0:
343-
# raise mip.InvalidLinExpr(
344-
# "An empty linear expression cannot be used as a constraint."
345-
# )
341+
346342
return self.constrs.add(lin_expr, name, priority)
347343

348344
def add_lazy_constr(self: "Model", expr: "mip.LinExpr"):

scripts/buildCBCMacARM.sh

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
export MACOSX_DEPLOYMENT_TARGET="10.9"
2+
export CFLAGS="-fPIC -Ofast -DNDEBUG -ffast-math -mmacosx-version-min=10.9"
3+
export CXXFLAGS="-fPIC -Og -DNDEBUG -ffast-math -std=c++11 -stdlib=libc++ -mmacosx-version-min=10.9"
4+
export F77FLAGS="-fPIC -Ofast -DNDEBUG -ffast-math"
5+
export LDFLAGS="-fPIC -Ofast -DNDEBUG -ffast-math"
6+
7+
DIR=`pwd`
8+
OUTDIR="/opt/cbc/bin"
9+
export PKG_CONFIG_PATH="${OUTDIR}/lib/pkgconfig/:${PKG_CONFIG_PATH}"
10+
11+
echo
12+
echo "Making and installing Glpk"
13+
cd ${DIR}/ThirdParty-Glpk
14+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
15+
git pull
16+
make
17+
make install
18+
19+
echo
20+
echo "Making and installing Lapack"
21+
cd ${DIR}/ThirdParty-Lapack
22+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
23+
git pull
24+
make
25+
make install
26+
27+
echo
28+
echo "Making and installing Blas"
29+
cd ${DIR}/ThirdParty-Blas
30+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
31+
git pull
32+
make
33+
make install
34+
35+
echo
36+
echo "Making and installing CoinUtils"
37+
cd ${DIR}/CoinUtils
38+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
39+
git pull
40+
make
41+
make install
42+
43+
echo
44+
echo "Making and installing Osi"
45+
cd ${DIR}/Osi
46+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
47+
git pull
48+
make
49+
make install
50+
51+
echo
52+
echo "Making and installing Clp"
53+
cd ${DIR}/Clp
54+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
55+
git pull
56+
make
57+
make install
58+
59+
echo
60+
echo "Making and installing Cgl"
61+
cd ${DIR}/Cgl
62+
./configure --prefix=${OUTDIR}/ --enable-static --disable-shared
63+
git pull
64+
make
65+
make install
66+
67+
echo
68+
echo "Making and installing Cbc"
69+
cd ${DIR}/Cbc
70+
./configure --prefix=${OUTDIR}/ --enable-cbc-parallel --enable-static --disable-shared
71+
git pull
72+
make
73+
make install
74+
75+
echo
76+
echo "Compiling dynamic library"
77+
cd ${DIR}
78+
clang++ -shared -Ofast -fPIC -o cbc-c-darwin-arm64.dylib \
79+
-I${OUTDIR}/include/coin-or/ -I${OUTDIR}/include/coin-or/glpk -I${OUTDIR}/include/coin \
80+
-L${OUTDIR}/lib \
81+
./Cbc/src/Cbc_C_Interface.cpp \
82+
-lCbc -lCgl -lClp -lCoinUtils -lOsi -lOsiCbc -lOsiClp -lOsiGlpk \
83+
-lcoinblas -lcoinglpk -lcoinlapack \
84+
-lbz2 -lz -llapack ${CXXFLAGS} -stdlib=libc++ -lreadline
85+
echo "Done!"

0 commit comments

Comments
 (0)