Skip to content

Commit

Permalink
Merge pull request #197 from fit-alessandro-berti/upstream-release
Browse files Browse the repository at this point in the history
Integrated support for CVXOPT solving
  • Loading branch information
fit-sebastiaan-van-zelst authored Jan 18, 2021
2 parents 7f77e8e + fd66fa5 commit 6c81fe6
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 10 deletions.
6 changes: 0 additions & 6 deletions pm4py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@

time.clock = time.process_time

try:
import pm4pycvxopt
except:
pass


from pm4py import util, objects, statistics, algo, visualization, evaluation, simulation

if pkgutil.find_loader("scipy"):
Expand Down
37 changes: 34 additions & 3 deletions pm4py/util/lp/solver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from pm4py.util.lp.parameters import Parameters
import pkgutil

import os

# not available in the latest version of PM4Py
CVXOPT = "cvxopt"
Expand All @@ -11,7 +10,7 @@
ORTOOLS_SOLVER = "ortools_solver"

# max allowed heuristics value (27/10/2019, due to the numerical instability of some of our solvers)
MAX_ALLOWED_HEURISTICS = 10**15
MAX_ALLOWED_HEURISTICS = 10 ** 15

VERSIONS_APPLY = {}
VERSIONS_GET_PRIM_OBJ = {}
Expand All @@ -38,6 +37,38 @@

DEFAULT_LP_SOLVER_VARIANT = ORTOOLS_SOLVER

if pkgutil.find_loader("cvxopt"):
from pm4py.util.lp.variants import cvxopt_solver, cvxopt_solver_custom_align, cvxopt_solver_custom_align_ilp, \
cvxopt_solver_custom_align_arm

custom_solver = cvxopt_solver_custom_align
try:
# for ARM-based Linux, we need to use a different call to GLPK
if "arm" in str(os.uname()[-1]):
custom_solver = cvxopt_solver
except:
pass

CVXOPT = "cvxopt"
CVXOPT_SOLVER_CUSTOM_ALIGN = "cvxopt_solver_custom_align"
CVXOPT_SOLVER_CUSTOM_ALIGN_ILP = "cvxopt_solver_custom_align_ilp"

VERSIONS_APPLY[CVXOPT] = cvxopt_solver.apply
VERSIONS_GET_PRIM_OBJ[CVXOPT] = cvxopt_solver.get_prim_obj_from_sol
VERSIONS_GET_POINTS_FROM_SOL[CVXOPT] = cvxopt_solver.get_points_from_sol

VERSIONS_APPLY[CVXOPT_SOLVER_CUSTOM_ALIGN] = custom_solver.apply
VERSIONS_GET_PRIM_OBJ[CVXOPT_SOLVER_CUSTOM_ALIGN] = custom_solver.get_prim_obj_from_sol
VERSIONS_GET_POINTS_FROM_SOL[CVXOPT_SOLVER_CUSTOM_ALIGN] = custom_solver.get_points_from_sol

VERSIONS_APPLY[CVXOPT_SOLVER_CUSTOM_ALIGN_ILP] = cvxopt_solver_custom_align_ilp.apply
VERSIONS_GET_PRIM_OBJ[
CVXOPT_SOLVER_CUSTOM_ALIGN_ILP] = cvxopt_solver_custom_align_ilp.get_prim_obj_from_sol
VERSIONS_GET_POINTS_FROM_SOL[
CVXOPT_SOLVER_CUSTOM_ALIGN_ILP] = cvxopt_solver_custom_align_ilp.get_points_from_sol

DEFAULT_LP_SOLVER_VARIANT = CVXOPT_SOLVER_CUSTOM_ALIGN


def apply(c, Aub, bub, Aeq, beq, parameters=None, variant=DEFAULT_LP_SOLVER_VARIANT):
"""
Expand Down
111 changes: 111 additions & 0 deletions pm4py/util/lp/variants/cvxopt_solver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import sys

from cvxopt import matrix, solvers


def apply(c, Aub, bub, Aeq, beq, parameters=None):
"""
Gets the overall solution of the problem
Parameters
------------
c
c parameter of the algorithm
Aub
A_ub parameter of the algorithm
bub
b_ub parameter of the algorithm
Aeq
A_eq parameter of the algorithm
beq
b_eq parameter of the algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
sol
Solution of the LP problem by the given algorithm
"""
if parameters is None:
parameters = {}

solver = parameters["solver"] if "solver" in parameters else None

c = matrix(c)
Aub = matrix(Aub)
bub = matrix(bub)
if Aeq is not None:
Aeq = matrix(Aeq)
if beq is not None:
beq = matrix(beq)

solvers.options['glpk'] = {}
solvers.options['glpk']['LPX_K_MSGLEV'] = 0
solvers.options['glpk']['msg_lev'] = 'GLP_MSG_OFF'
solvers.options['glpk']['show_progress'] = False
solvers.options['glpk']['presolve'] = "GLP_ON"
solvers.options['glpk']['meth'] = "GLP_PRIMAL"
solvers.options['msg_lev'] = 'GLP_MSG_OFF'
solvers.options['show_progress'] = False

if solver:
sol = solvers.lp(c, Aub, bub, A=Aeq, b=beq, solver=solver)
else:
sol = solvers.lp(c, Aub, bub, A=Aeq, b=beq)

return sol


def get_prim_obj_from_sol(sol, parameters=None):
"""
Gets the primal objective from the solution of the LP problem
Parameters
-------------
sol
Solution of the ILP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
prim_obj
Primal objective
"""
if parameters is None:
parameters = {}

return sol["primal objective"]


def get_points_from_sol(sol, parameters=None):
"""
Gets the points from the solution
Parameters
-------------
sol
Solution of the LP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
points
Point of the solution
"""
if parameters is None:
parameters = {}

maximize = parameters["maximize"] if "maximize" in parameters else False
return_when_none = parameters["return_when_none"] if "return_when_none" in parameters else False
var_corr = parameters["var_corr"] if "var_corr" in parameters else {}

if sol and 'x' in sol and sol['x'] is not None:
return list(sol['x'])
else:
if return_when_none:
if maximize:
return [sys.float_info.max] * len(list(var_corr.keys()))
return [sys.float_info.min] * len(list(var_corr.keys()))
106 changes: 106 additions & 0 deletions pm4py/util/lp/variants/cvxopt_solver_custom_align.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import sys

from cvxopt import blas
from cvxopt import glpk

this_options = {}
this_options["LPX_K_MSGLEV"] = 0
this_options["msg_lev"] = "GLP_MSG_OFF"
this_options["show_progress"] = False
this_options["presolve"] = "GLP_ON"
this_options["tol_bnd"] = 10**-5
this_options["tol_piv"] = 10**-5
this_options["obj_ll"] = 10**-5
this_options["obj_ul"] = 10**-5
this_options["obj_ul"] = 10**-5


def custom_solve_lp(c, G, h, A, b):
status, x, z, y = glpk.lp(c, G, h, A, b, options=this_options)

if status == 'optimal':
pcost = blas.dot(c, x)
else:
pcost = None

return {'status': status, 'x': x, 'primal objective': pcost}


def apply(c, Aub, bub, Aeq, beq, parameters=None):
"""
Gets the overall solution of the problem
Parameters
------------
c
c parameter of the algorithm
Aub
A_ub parameter of the algorithm
bub
b_ub parameter of the algorithm
Aeq
A_eq parameter of the algorithm
beq
b_eq parameter of the algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
sol
Solution of the LP problem by the given algorithm
"""
sol = custom_solve_lp(c, Aub, bub, Aeq, beq)

return sol


def get_prim_obj_from_sol(sol, parameters=None):
"""
Gets the primal objective from the solution of the LP problem
Parameters
-------------
sol
Solution of the ILP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
prim_obj
Primal objective
"""
return sol["primal objective"]


def get_points_from_sol(sol, parameters=None):
"""
Gets the points from the solution
Parameters
-------------
sol
Solution of the LP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
points
Point of the solution
"""
if parameters is None:
parameters = {}

maximize = parameters["maximize"] if "maximize" in parameters else False
return_when_none = parameters["return_when_none"] if "return_when_none" in parameters else False
var_corr = parameters["var_corr"] if "var_corr" in parameters else {}

if sol and 'x' in sol and sol['x'] is not None:
return list(sol['x'])
else:
if return_when_none:
if maximize:
return [sys.float_info.max] * len(list(var_corr.keys()))
return [sys.float_info.min] * len(list(var_corr.keys()))
95 changes: 95 additions & 0 deletions pm4py/util/lp/variants/cvxopt_solver_custom_align_arm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import sys

from cvxopt import blas
from cvxopt import glpk


def custom_solve_lp(c, G, h, A, b):
status, x, z, y = glpk.lp(c, G, h, A, b)

if status == 'optimal':
pcost = blas.dot(c, x)
else:
pcost = None

return {'status': status, 'x': x, 'primal objective': pcost}


def apply(c, Aub, bub, Aeq, beq, parameters=None):
"""
Gets the overall solution of the problem
Parameters
------------
c
c parameter of the algorithm
Aub
A_ub parameter of the algorithm
bub
b_ub parameter of the algorithm
Aeq
A_eq parameter of the algorithm
beq
b_eq parameter of the algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
sol
Solution of the LP problem by the given algorithm
"""
sol = custom_solve_lp(c, Aub, bub, Aeq, beq)

return sol


def get_prim_obj_from_sol(sol, parameters=None):
"""
Gets the primal objective from the solution of the LP problem
Parameters
-------------
sol
Solution of the ILP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
prim_obj
Primal objective
"""
return sol["primal objective"]


def get_points_from_sol(sol, parameters=None):
"""
Gets the points from the solution
Parameters
-------------
sol
Solution of the LP problem by the given algorithm
parameters
Possible parameters of the algorithm
Returns
-------------
points
Point of the solution
"""
if parameters is None:
parameters = {}

maximize = parameters["maximize"] if "maximize" in parameters else False
return_when_none = parameters["return_when_none"] if "return_when_none" in parameters else False
var_corr = parameters["var_corr"] if "var_corr" in parameters else {}

if sol and 'x' in sol and sol['x'] is not None:
return list(sol['x'])
else:
if return_when_none:
if maximize:
return [sys.float_info.max] * len(list(var_corr.keys()))
return [sys.float_info.min] * len(list(var_corr.keys()))
Loading

0 comments on commit 6c81fe6

Please sign in to comment.