feat: 初始化Easy Patch插件及依赖文件
- 添加Blender插件核心文件:__init__.py、ui.py、property.py、preference.py - 添加插件工具模块:g.py、loop.py、generate_loop.py、const.py、op.py - 添加翻译工具:utils/trans.py - 添加PuLP线性规划库及其依赖文件,包括CBC求解器二进制文件 - 添加.gitignore和VSCode配置文件
This commit is contained in:
409
utils/pulp/apis/glpk_api.py
Normal file
409
utils/pulp/apis/glpk_api.py
Normal file
@@ -0,0 +1,409 @@
|
||||
# PuLP : Python LP Modeler
|
||||
# Version 1.4.2
|
||||
|
||||
# Copyright (c) 2002-2005, Jean-Sebastien Roy (js@jeannot.org)
|
||||
# Modifications Copyright (c) 2007- Stuart Anthony Mitchell (s.mitchell@auckland.ac.nz)
|
||||
# $Id:solvers.py 1791 2008-04-23 22:54:34Z smit023 $
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""
|
||||
|
||||
from .core import LpSolver_CMD, LpSolver, subprocess, PulpSolverError, clock
|
||||
from .core import glpk_path, operating_system, log
|
||||
import os
|
||||
from .. import constants
|
||||
|
||||
|
||||
class GLPK_CMD(LpSolver_CMD):
|
||||
"""The GLPK LP solver"""
|
||||
|
||||
name = "GLPK_CMD"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path=None,
|
||||
keepFiles=False,
|
||||
mip=True,
|
||||
msg=True,
|
||||
options=None,
|
||||
timeLimit=None,
|
||||
):
|
||||
"""
|
||||
:param bool mip: if False, assume LP even if integer variables
|
||||
:param bool msg: if False, no log is shown
|
||||
:param float timeLimit: maximum time for solver (in seconds)
|
||||
:param list options: list of additional options to pass to solver
|
||||
:param bool keepFiles: if True, files are saved in the current directory and not deleted after solving
|
||||
:param str path: path to the solver binary
|
||||
"""
|
||||
LpSolver_CMD.__init__(
|
||||
self,
|
||||
mip=mip,
|
||||
msg=msg,
|
||||
timeLimit=timeLimit,
|
||||
options=options,
|
||||
path=path,
|
||||
keepFiles=keepFiles,
|
||||
)
|
||||
|
||||
def defaultPath(self):
|
||||
return self.executableExtension(glpk_path)
|
||||
|
||||
def available(self):
|
||||
"""True if the solver is available"""
|
||||
return self.executable(self.path)
|
||||
|
||||
def actualSolve(self, lp):
|
||||
"""Solve a well formulated lp problem"""
|
||||
if not self.executable(self.path):
|
||||
raise PulpSolverError("PuLP: cannot execute " + self.path)
|
||||
tmpLp, tmpSol = self.create_tmp_files(lp.name, "lp", "sol")
|
||||
lp.writeLP(tmpLp, writeSOS=0)
|
||||
proc = ["glpsol", "--cpxlp", tmpLp, "-o", tmpSol]
|
||||
if self.timeLimit:
|
||||
proc.extend(["--tmlim", str(self.timeLimit)])
|
||||
if not self.mip:
|
||||
proc.append("--nomip")
|
||||
proc.extend(self.options)
|
||||
|
||||
self.solution_time = clock()
|
||||
if not self.msg:
|
||||
proc[0] = self.path
|
||||
pipe = open(os.devnull, "w")
|
||||
if operating_system == "win":
|
||||
# Prevent flashing windows if used from a GUI application
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
rc = subprocess.call(
|
||||
proc, stdout=pipe, stderr=pipe, startupinfo=startupinfo
|
||||
)
|
||||
else:
|
||||
rc = subprocess.call(proc, stdout=pipe, stderr=pipe)
|
||||
if rc:
|
||||
raise PulpSolverError(
|
||||
"PuLP: Error while trying to execute " + self.path
|
||||
)
|
||||
pipe.close()
|
||||
else:
|
||||
if os.name != "nt":
|
||||
rc = os.spawnvp(os.P_WAIT, self.path, proc)
|
||||
else:
|
||||
rc = os.spawnv(os.P_WAIT, self.executable(self.path), proc)
|
||||
if rc == 127:
|
||||
raise PulpSolverError(
|
||||
"PuLP: Error while trying to execute " + self.path
|
||||
)
|
||||
self.solution_time += clock()
|
||||
|
||||
if not os.path.exists(tmpSol):
|
||||
raise PulpSolverError("PuLP: Error while executing " + self.path)
|
||||
status, values = self.readsol(tmpSol)
|
||||
lp.assignVarsVals(values)
|
||||
lp.assignStatus(status)
|
||||
self.delete_tmp_files(tmpLp, tmpSol)
|
||||
return status
|
||||
|
||||
def readsol(self, filename):
|
||||
"""Read a GLPK solution file"""
|
||||
with open(filename) as f:
|
||||
f.readline()
|
||||
rows = int(f.readline().split()[1])
|
||||
cols = int(f.readline().split()[1])
|
||||
f.readline()
|
||||
statusString = f.readline()[12:-1]
|
||||
glpkStatus = {
|
||||
"INTEGER OPTIMAL": constants.LpStatusOptimal,
|
||||
"INTEGER NON-OPTIMAL": constants.LpStatusOptimal,
|
||||
"OPTIMAL": constants.LpStatusOptimal,
|
||||
"INFEASIBLE (FINAL)": constants.LpStatusInfeasible,
|
||||
"INTEGER UNDEFINED": constants.LpStatusUndefined,
|
||||
"UNBOUNDED": constants.LpStatusUnbounded,
|
||||
"UNDEFINED": constants.LpStatusUndefined,
|
||||
"INTEGER EMPTY": constants.LpStatusInfeasible,
|
||||
}
|
||||
if statusString not in glpkStatus:
|
||||
raise PulpSolverError("Unknown status returned by GLPK")
|
||||
status = glpkStatus[statusString]
|
||||
isInteger = statusString in [
|
||||
"INTEGER NON-OPTIMAL",
|
||||
"INTEGER OPTIMAL",
|
||||
"INTEGER UNDEFINED",
|
||||
"INTEGER EMPTY",
|
||||
]
|
||||
values = {}
|
||||
for i in range(4):
|
||||
f.readline()
|
||||
for i in range(rows):
|
||||
line = f.readline().split()
|
||||
if len(line) == 2:
|
||||
f.readline()
|
||||
for i in range(3):
|
||||
f.readline()
|
||||
for i in range(cols):
|
||||
line = f.readline().split()
|
||||
name = line[1]
|
||||
if len(line) == 2:
|
||||
line = [0, 0] + f.readline().split()
|
||||
if isInteger:
|
||||
if line[2] == "*":
|
||||
value = int(float(line[3]))
|
||||
else:
|
||||
value = float(line[2])
|
||||
else:
|
||||
value = float(line[3])
|
||||
values[name] = value
|
||||
return status, values
|
||||
|
||||
|
||||
GLPK = GLPK_CMD
|
||||
|
||||
# get the glpk name in global scope
|
||||
glpk = None
|
||||
|
||||
|
||||
class PYGLPK(LpSolver):
|
||||
"""
|
||||
The glpk LP/MIP solver (via its python interface)
|
||||
|
||||
Copyright Christophe-Marie Duquesne 2012
|
||||
|
||||
The glpk variables are available (after a solve) in var.solverVar
|
||||
The glpk constraints are available in constraint.solverConstraint
|
||||
The Model is in prob.solverModel
|
||||
"""
|
||||
|
||||
name = "PYGLPK"
|
||||
|
||||
try:
|
||||
# import the model into the global scope
|
||||
global glpk
|
||||
import glpk.glpkpi as glpk
|
||||
except:
|
||||
|
||||
def available(self):
|
||||
"""True if the solver is available"""
|
||||
return False
|
||||
|
||||
def actualSolve(self, lp, callback=None):
|
||||
"""Solve a well formulated lp problem"""
|
||||
raise PulpSolverError("GLPK: Not Available")
|
||||
|
||||
else:
|
||||
|
||||
def __init__(
|
||||
self, mip=True, msg=True, timeLimit=None, epgap=None, **solverParams
|
||||
):
|
||||
"""
|
||||
Initializes the glpk solver.
|
||||
|
||||
@param mip: if False the solver will solve a MIP as an LP
|
||||
@param msg: displays information from the solver to stdout
|
||||
@param timeLimit: not handled
|
||||
@param epgap: not handled
|
||||
@param solverParams: not handled
|
||||
"""
|
||||
LpSolver.__init__(self, mip, msg)
|
||||
if not self.msg:
|
||||
glpk.glp_term_out(glpk.GLP_OFF)
|
||||
|
||||
def findSolutionValues(self, lp):
|
||||
prob = lp.solverModel
|
||||
if self.mip and self.hasMIPConstraints(lp.solverModel):
|
||||
solutionStatus = glpk.glp_mip_status(prob)
|
||||
else:
|
||||
solutionStatus = glpk.glp_get_status(prob)
|
||||
glpkLpStatus = {
|
||||
glpk.GLP_OPT: constants.LpStatusOptimal,
|
||||
glpk.GLP_UNDEF: constants.LpStatusUndefined,
|
||||
glpk.GLP_FEAS: constants.LpStatusOptimal,
|
||||
glpk.GLP_INFEAS: constants.LpStatusInfeasible,
|
||||
glpk.GLP_NOFEAS: constants.LpStatusInfeasible,
|
||||
glpk.GLP_UNBND: constants.LpStatusUnbounded,
|
||||
}
|
||||
# populate pulp solution values
|
||||
for var in lp.variables():
|
||||
if self.mip and self.hasMIPConstraints(lp.solverModel):
|
||||
var.varValue = glpk.glp_mip_col_val(prob, var.glpk_index)
|
||||
else:
|
||||
var.varValue = glpk.glp_get_col_prim(prob, var.glpk_index)
|
||||
var.dj = glpk.glp_get_col_dual(prob, var.glpk_index)
|
||||
# put pi and slack variables against the constraints
|
||||
for constr in lp.constraints.values():
|
||||
if self.mip and self.hasMIPConstraints(lp.solverModel):
|
||||
row_val = glpk.glp_mip_row_val(prob, constr.glpk_index)
|
||||
else:
|
||||
row_val = glpk.glp_get_row_prim(prob, constr.glpk_index)
|
||||
constr.slack = -constr.constant - row_val
|
||||
constr.pi = glpk.glp_get_row_dual(prob, constr.glpk_index)
|
||||
lp.resolveOK = True
|
||||
for var in lp.variables():
|
||||
var.isModified = False
|
||||
status = glpkLpStatus.get(solutionStatus, constants.LpStatusUndefined)
|
||||
lp.assignStatus(status)
|
||||
return status
|
||||
|
||||
def available(self):
|
||||
"""True if the solver is available"""
|
||||
return True
|
||||
|
||||
def hasMIPConstraints(self, solverModel):
|
||||
return (
|
||||
glpk.glp_get_num_int(solverModel) > 0
|
||||
or glpk.glp_get_num_bin(solverModel) > 0
|
||||
)
|
||||
|
||||
def callSolver(self, lp, callback=None):
|
||||
"""Solves the problem with glpk"""
|
||||
self.solveTime = -clock()
|
||||
glpk.glp_adv_basis(lp.solverModel, 0)
|
||||
glpk.glp_simplex(lp.solverModel, None)
|
||||
if self.mip and self.hasMIPConstraints(lp.solverModel):
|
||||
status = glpk.glp_get_status(lp.solverModel)
|
||||
if status in (glpk.GLP_OPT, glpk.GLP_UNDEF, glpk.GLP_FEAS):
|
||||
glpk.glp_intopt(lp.solverModel, None)
|
||||
self.solveTime += clock()
|
||||
|
||||
def buildSolverModel(self, lp):
|
||||
"""
|
||||
Takes the pulp lp model and translates it into a glpk model
|
||||
"""
|
||||
log.debug("create the glpk model")
|
||||
prob = glpk.glp_create_prob()
|
||||
glpk.glp_set_prob_name(prob, lp.name)
|
||||
log.debug("set the sense of the problem")
|
||||
if lp.sense == constants.LpMaximize:
|
||||
glpk.glp_set_obj_dir(prob, glpk.GLP_MAX)
|
||||
log.debug("add the constraints to the problem")
|
||||
glpk.glp_add_rows(prob, len(list(lp.constraints.keys())))
|
||||
for i, v in enumerate(lp.constraints.items(), start=1):
|
||||
name, constraint = v
|
||||
glpk.glp_set_row_name(prob, i, name)
|
||||
if constraint.sense == constants.LpConstraintLE:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob, i, glpk.GLP_UP, 0.0, -constraint.constant
|
||||
)
|
||||
elif constraint.sense == constants.LpConstraintGE:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob, i, glpk.GLP_LO, -constraint.constant, 0.0
|
||||
)
|
||||
elif constraint.sense == constants.LpConstraintEQ:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob, i, glpk.GLP_FX, -constraint.constant, -constraint.constant
|
||||
)
|
||||
else:
|
||||
raise PulpSolverError("Detected an invalid constraint type")
|
||||
constraint.glpk_index = i
|
||||
log.debug("add the variables to the problem")
|
||||
glpk.glp_add_cols(prob, len(lp.variables()))
|
||||
for j, var in enumerate(lp.variables(), start=1):
|
||||
glpk.glp_set_col_name(prob, j, var.name)
|
||||
lb = 0.0
|
||||
ub = 0.0
|
||||
t = glpk.GLP_FR
|
||||
if not var.lowBound is None:
|
||||
lb = var.lowBound
|
||||
t = glpk.GLP_LO
|
||||
if not var.upBound is None:
|
||||
ub = var.upBound
|
||||
t = glpk.GLP_UP
|
||||
if not var.upBound is None and not var.lowBound is None:
|
||||
if ub == lb:
|
||||
t = glpk.GLP_FX
|
||||
else:
|
||||
t = glpk.GLP_DB
|
||||
glpk.glp_set_col_bnds(prob, j, t, lb, ub)
|
||||
if var.cat == constants.LpInteger:
|
||||
glpk.glp_set_col_kind(prob, j, glpk.GLP_IV)
|
||||
assert glpk.glp_get_col_kind(prob, j) == glpk.GLP_IV
|
||||
var.glpk_index = j
|
||||
log.debug("set the objective function")
|
||||
for var in lp.variables():
|
||||
value = lp.objective.get(var)
|
||||
if value:
|
||||
glpk.glp_set_obj_coef(prob, var.glpk_index, value)
|
||||
log.debug("set the problem matrix")
|
||||
for constraint in lp.constraints.values():
|
||||
l = len(list(constraint.items()))
|
||||
ind = glpk.intArray(l + 1)
|
||||
val = glpk.doubleArray(l + 1)
|
||||
for j, v in enumerate(constraint.items(), start=1):
|
||||
var, value = v
|
||||
ind[j] = var.glpk_index
|
||||
val[j] = value
|
||||
glpk.glp_set_mat_row(prob, constraint.glpk_index, l, ind, val)
|
||||
lp.solverModel = prob
|
||||
# glpk.glp_write_lp(prob, None, "glpk.lp")
|
||||
|
||||
def actualSolve(self, lp, callback=None):
|
||||
"""
|
||||
Solve a well formulated lp problem
|
||||
|
||||
creates a glpk model, variables and constraints and attaches
|
||||
them to the lp model which it then solves
|
||||
"""
|
||||
self.buildSolverModel(lp)
|
||||
# set the initial solution
|
||||
log.debug("Solve the Model using glpk")
|
||||
self.callSolver(lp, callback=callback)
|
||||
# get the solution information
|
||||
solutionStatus = self.findSolutionValues(lp)
|
||||
for var in lp.variables():
|
||||
var.modified = False
|
||||
for constraint in lp.constraints.values():
|
||||
constraint.modified = False
|
||||
return solutionStatus
|
||||
|
||||
def actualResolve(self, lp, callback=None):
|
||||
"""
|
||||
Solve a well formulated lp problem
|
||||
|
||||
uses the old solver and modifies the rhs of the modified
|
||||
constraints
|
||||
"""
|
||||
prob = lp.solverModel
|
||||
log.debug("Resolve the Model using glpk")
|
||||
for constraint in lp.constraints.values():
|
||||
i = constraint.glpk_index
|
||||
if constraint.modified:
|
||||
if constraint.sense == constants.LpConstraintLE:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob, i, glpk.GLP_UP, 0.0, -constraint.constant
|
||||
)
|
||||
elif constraint.sense == constants.LpConstraintGE:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob, i, glpk.GLP_LO, -constraint.constant, 0.0
|
||||
)
|
||||
elif constraint.sense == constants.LpConstraintEQ:
|
||||
glpk.glp_set_row_bnds(
|
||||
prob,
|
||||
i,
|
||||
glpk.GLP_FX,
|
||||
-constraint.constant,
|
||||
-constraint.constant,
|
||||
)
|
||||
else:
|
||||
raise PulpSolverError("Detected an invalid constraint type")
|
||||
self.callSolver(lp, callback=callback)
|
||||
# get the solution information
|
||||
solutionStatus = self.findSolutionValues(lp)
|
||||
for var in lp.variables():
|
||||
var.modified = False
|
||||
for constraint in lp.constraints.values():
|
||||
constraint.modified = False
|
||||
return solutionStatus
|
||||
Reference in New Issue
Block a user