ojAlgo可以整合ortools,使用ojAlgo来进行约束条件的模型构造,使用ortools来进行求解。因为我们一开始使用的是ojAlgo,这样可以更小幅度的修改,只需要求解时使用ortools就可以
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.ojalgo</groupId > <artifactId > ojalgo</artifactId > <version > 51.4.1</version > </dependency > <dependency > <groupId > com.google.ortools</groupId > <artifactId > ortools-java</artifactId > <version > 8.2.9025</version > </dependency >
还是以求解 5x1 + 6x2 + 23x3 + 5x4 + 24x5 + 6x6 + 23x7 + 5x8
的最小值为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 ExpressionsBasedModel model = new ExpressionsBasedModel(); model.addVariable(Variable.makeInteger("x" +1 ).lower(0 ).weight(5 )); model.addVariable(Variable.makeInteger("x" +2 ).lower(0 ).weight(6 )); model.addVariable(Variable.makeInteger("x" +3 ).lower(0 ).weight(23 )); model.addVariable(Variable.makeInteger("x" +4 ).lower(0 ).weight(5 )); model.addVariable(Variable.makeInteger("x" +5 ).lower(0 ).weight(24 )); model.addVariable(Variable.makeInteger("x" +6 ).lower(0 ).weight(6 )); model.addVariable(Variable.makeInteger("x" +7 ).lower(0 ).weight(23 )); model.addVariable(Variable.makeInteger("x" +8 ).lower(0 ).weight(5 )); Expression expression = model.addExpression("e" + 1 ); expression.set(0 ,2 ); expression.set(1 ,1 ); expression.set(2 ,1 ); expression.set(3 ,1 ); expression.lower(100 ); Expression expression2 = model.addExpression("e" + 2 ); expression2.set(1 ,2 ); expression2.set(2 ,1 ); expression2.set(4 ,3 ); expression2.set(5 ,2 ); expression2.set(6 ,1 ); expression2.lower(150 ); Expression expression3 = model.addExpression("e" + 3 ); expression3.set(0 ,1 ); expression3.set(2 ,1 ); expression3.set(3 ,3 ); expression3.set(5 ,2 ); expression3.set(6 ,3 ); expression3.set(7 ,5 ); expression3.lower(100 ); SolverORTools tools = SolverORTools.INTEGRATION.build(model); Optimisation.Result result = tools.solve(null ); System.out.println(result);
可以发现,业务部分上面构造model的地方一点都没有动,只是改了solve的地方
SolverORTools是自己写的Solver,这点ojAlgo做的还是比较好的,插件式的求解器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 public final class SolverORTools implements Optimisation .Solver { public static final SolverORTools.Integration INTEGRATION = new SolverORTools.Integration(); static final SolverORTools.Configurator DEFAULT = (solver, options) -> { }; private final MPSolver mySolver; private final MPVariable[] myVariables; static MPSolver newSolver (ExpressionsBasedModel model) { return MPSolver.createSolver("SCIP" ); } static State translate (MPSolver.ResultStatus status) { switch (status) { case NOT_SOLVED: return State.UNEXPLORED; case ABNORMAL: return State.FAILED; case UNBOUNDED: case INFEASIBLE: return State.INFEASIBLE; case FEASIBLE: return State.FEASIBLE; case OPTIMAL: return State.OPTIMAL; default : return State.FAILED; } } SolverORTools(MPSolver solver, MPVariable[] variables) { this .myVariables = variables; this .mySolver = solver; } public void dispose () { if (mySolver != null ) { mySolver.delete(); } } public Result solve (Result kickStarter) { try { State retState = translate(mySolver.solve()); double retValue = mySolver.objective().value(); double [] retSolution = new double [myVariables.length]; if (retState.isFeasible()) { for (int i = 0 ; i < retSolution.length; ++i) { retSolution[i] = myVariables[i].solutionValue(); } } return Result.of(retValue, retState, retSolution); } catch (Exception e){ e.printStackTrace(); return null ; } } static { Loader.loadNativeLibraries(); } public static final class Integration extends org .ojalgo .optimisation .ExpressionsBasedModel .Integration <SolverORTools > { Integration() { } public SolverORTools build (ExpressionsBasedModel model) { boolean mip = model.isAnyVariableInteger(); MPSolver solver = SolverORTools.newSolver(model); List<Variable> mVars = model.getVariables(); int nbVars = mVars.size(); double posInf = MPSolver.infinity(); double negInf = -posInf; MPVariable[] sVars = new MPVariable[nbVars]; for (int i = 0 ; i < nbVars; ++i) { Variable mVar = mVars.get(i); BigDecimal lb = mVar.getLowerLimit() == null ? BigDecimal.ZERO : mVar.getLowerLimit(); BigDecimal ub = mVar.getUpperLimit() == null ? BigDecimal.valueOf(Integer.MAX_VALUE) : mVar.getUpperLimit(); boolean integer = mip && mVar.isInteger(); String name = mVar.getName(); sVars[i] = solver.makeVar(lb.doubleValue(), ub.doubleValue(), integer, name); } List<Expression> constraints = model.constraints().collect(Collectors.toList()); model.constraints().forEach((mConstr) -> { BigDecimal lb = mConstr.getLowerLimit() == null ? BigDecimal.valueOf(Integer.MIN_VALUE) : mConstr.getLowerLimit(); BigDecimal ub = mConstr.getUpperLimit() == null ? BigDecimal.valueOf(Integer.MAX_VALUE) : mConstr.getUpperLimit(); String name = mConstr.getName(); MPConstraint sConstr = solver.makeConstraint(lb.doubleValue(), ub.doubleValue(), name); Iterator<Structure1D.IntIndex> iterator = mConstr.getLinearKeySet().iterator(); while (iterator.hasNext()) { Structure1D.IntIndex key = iterator.next(); sConstr.setCoefficient(sVars[key.index], mConstr.get(key).doubleValue()); } }); Expression mObj = model.objective(); MPObjective sObj = solver.objective(); sObj.setOptimizationDirection(model.getOptimisationSense() == Sense.MAX); Iterator var21 = mObj.getLinearKeySet().iterator(); while (var21.hasNext()) { IntIndex key = (IntIndex)var21.next(); sObj.setCoefficient(sVars[key.index], mObj.get(key).doubleValue()); } SolverORTools.DEFAULT.configure(solver, model.options); model.options.getConfigurator(SolverORTools.Configurator.class).ifPresent((c) -> { c.configure(solver, model.options); }); return new SolverORTools(solver, sVars); } public boolean isCapable (ExpressionsBasedModel model) { return !model.isAnyExpressionQuadratic(); } protected boolean isSolutionMapped () { return false ; } } @FunctionalInterface public interface Configurator { void configure (MPSolver solver, Options options) ; } }
底层使用的是SCIP
1 MPSolver.createSolver("SCIP" )
为了解决混合整数规划问题,OR-Tools也提供了几种工具:
MPSolver:MPSolver接口可用于解决LP问题和MIP问题,同样包含几个第三方MIP求解器(CBC、SCIP、GLPK、Gurobi)。OR-Tools实际上提供的是统一的求解器接口,内部连接的求解器可以自己配置,默认使用CBC求解器。
CP-SAT:它是使用SAT(satisfiability)方法的约束规划求解器,是原始约束规划求解器(CP Solver)的高级版。为了提高计算速度,CP-SAT求解器仅处理整数,这意味着必须使用整数来定义优化问题,如果从具有非整数项约束的问题开始,则需要将约束乘以一个足够大的整数,以便所有项都是整数。
在这里我们使用了MPSolver接口,ortools内置了SCIP,不需要在服务器上安装SCIP(安装过程比较复杂),且集合了各种先进的优化算法,它所包含的求解器主要分为约束规划、线性和整数规划、车辆路径规划以及图论算法这四个基本求解器,能够按照优化问题的类型,提供相对应的不同类和接口。