Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExactOptimization/3.3/LinearProgramming/Algorithms/Solvers/Base/LinearSolver.cs @ 17180

Last change on this file since 17180 was 17180, checked in by swagner, 5 years ago

#2875: Removed years in copyrights

File size: 10.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.IO;
25using System.Linq;
26using System.Reflection;
27using System.Threading;
28using Google.OrTools.LinearSolver;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Data;
32using HeuristicLab.Optimization;
33using HeuristicLab.Parameters;
34using HEAL.Attic;
35
36namespace HeuristicLab.ExactOptimization.LinearProgramming {
37
38  [StorableType("D0657902-BE8B-4826-B832-FDA84E9B24C3")]
39  public class LinearSolver : ParameterizedNamedItem, ILinearSolver, IDisposable {
40
41    [Storable]
42    protected IValueParameter<EnumValue<ProblemType>> problemTypeParam;
43
44    protected Solver solver;
45
46    [Storable]
47    protected IFixedValueParameter<TextValue> solverSpecificParametersParam;
48
49    public static string LogDirectory { get; }
50
51    static LinearSolver() {
52      var solver = new Solver("", Solver.OptimizationProblemType.CbcMixedIntegerProgramming);
53      LogDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
54      Directory.CreateDirectory(LogDirectory);
55      solver.InitLogging(Assembly.GetExecutingAssembly().Location, LogDirectory);
56    }
57
58    public LinearSolver() {
59      Parameters.Add(problemTypeParam =
60        new ValueParameter<EnumValue<ProblemType>>(nameof(ProblemType),
61          new EnumValue<ProblemType>(ProblemType.MixedIntegerProgramming)));
62      Parameters.Add(solverSpecificParametersParam =
63        new FixedValueParameter<TextValue>(nameof(SolverSpecificParameters), new TextValue()));
64    }
65
66    [StorableConstructor]
67    protected LinearSolver(StorableConstructorFlag _) : base(_) { }
68
69    protected LinearSolver(LinearSolver original, Cloner cloner)
70      : base(original, cloner) {
71      problemTypeParam = cloner.Clone(original.problemTypeParam);
72      solverSpecificParametersParam = cloner.Clone(original.solverSpecificParametersParam);
73    }
74
75    public double DualTolerance { get; set; } = SolverParameters.DefaultDualTolerance;
76
77    public bool Incrementality { get; set; } =
78      SolverParameters.DefaultIncrementality == SolverParameters.IncrementalityValues.IncrementalityOn;
79
80    public SolverParameters.LpAlgorithmValues LpAlgorithm { get; set; }
81    protected virtual Solver.OptimizationProblemType OptimizationProblemType { get; }
82    public bool Presolve { get; set; } = SolverParameters.DefaultPresolve == SolverParameters.PresolveValues.PresolveOn;
83    public double PrimalTolerance { get; set; } = SolverParameters.DefaultPrimalTolerance;
84
85    public ProblemType ProblemType {
86      get => problemTypeParam.Value.Value;
87      set => problemTypeParam.Value.Value = value;
88    }
89
90    public IValueParameter<EnumValue<ProblemType>> ProblemTypeParameter => problemTypeParam;
91    public double RelativeGapTolerance { get; set; } = SolverParameters.DefaultRelativeMipGap;
92    public bool Scaling { get; set; }
93
94    public string SolverSpecificParameters {
95      get => solverSpecificParametersParam.Value.Value;
96      set => solverSpecificParametersParam.Value.Value = value;
97    }
98
99    public IFixedValueParameter<TextValue> SolverSpecificParametersParameter => solverSpecificParametersParam;
100    public virtual bool SupportsPause => true;
101    public virtual bool SupportsStop => true;
102    public virtual TimeSpan TimeLimit { get; set; } = TimeSpan.Zero;
103
104    public override IDeepCloneable Clone(Cloner cloner) => new LinearSolver(this, cloner);
105
106    public void Dispose() => solver?.Dispose();
107
108    public bool ExportAsLp(string fileName, bool obfuscated = false) {
109      var lpFormat = solver?.ExportModelAsLpFormat(obfuscated);
110      if (string.IsNullOrEmpty(lpFormat))
111        return false;
112      File.WriteAllText(fileName, lpFormat);
113      return true;
114    }
115
116    public bool ExportAsMps(string fileName, bool fixedFormat = false, bool obfuscated = false) {
117      var mpsFormat = solver?.ExportModelAsMpsFormat(fixedFormat, obfuscated);
118      if (string.IsNullOrEmpty(mpsFormat))
119        return false;
120      File.WriteAllText(fileName, mpsFormat);
121      return true;
122    }
123
124    public bool ExportAsProto(string fileName, ProtoWriteFormat writeFormat = ProtoWriteFormat.ProtoBinary) =>
125      solver != null && solver.ExportModelAsProtoFormat(fileName, (Google.OrTools.LinearSolver.ProtoWriteFormat)writeFormat);
126
127    public MPSolverResponseStatus ImportFromMps(string fileName, bool? fixedFormat) =>
128      solver?.ImportModelFromMpsFormat(fileName, fixedFormat.HasValue, fixedFormat ?? false) ??
129      (MPSolverResponseStatus)SolverResponseStatus.Abnormal;
130
131    public MPSolverResponseStatus ImportFromProto(string fileName) =>
132      solver?.ImportModelFromProtoFormat(fileName) ?? (MPSolverResponseStatus)SolverResponseStatus.Abnormal;
133
134    public bool InterruptSolve() => solver?.InterruptSolve() ?? false;
135
136    public virtual void Reset() {
137      solver?.Dispose();
138      solver = null;
139    }
140
141    public virtual void Solve(ILinearProblemDefinition problemDefintion,
142      ResultCollection results, CancellationToken cancellationToken) =>
143      Solve(problemDefintion, results);
144
145    public virtual void Solve(ILinearProblemDefinition problemDefinition,
146      ResultCollection results) =>
147      Solve(problemDefinition, results, TimeLimit);
148
149    public virtual void Solve(ILinearProblemDefinition problemDefinition, ResultCollection results,
150      TimeSpan timeLimit) {
151      if (solver == null) {
152        solver = CreateSolver(OptimizationProblemType);
153        problemDefinition.BuildModel(solver);
154      }
155
156      if (timeLimit > TimeSpan.Zero) {
157        solver.SetTimeLimit((long)timeLimit.TotalMilliseconds);
158      } else {
159        solver.SetTimeLimit(0);
160      }
161
162      ResultStatus resultStatus;
163
164      using (var parameters = new SolverParameters()) {
165        parameters.SetDoubleParam(SolverParameters.DoubleParam.RelativeMipGap, RelativeGapTolerance);
166        parameters.SetDoubleParam(SolverParameters.DoubleParam.PrimalTolerance, PrimalTolerance);
167        parameters.SetDoubleParam(SolverParameters.DoubleParam.DualTolerance, DualTolerance);
168        parameters.SetIntegerParam(SolverParameters.IntegerParam.Presolve,
169          (int)(Presolve ? SolverParameters.PresolveValues.PresolveOn : SolverParameters.PresolveValues.PresolveOff));
170        parameters.SetIntegerParam(SolverParameters.IntegerParam.Incrementality,
171          (int)(Incrementality ? SolverParameters.IncrementalityValues.IncrementalityOn : SolverParameters.IncrementalityValues.IncrementalityOff));
172        parameters.SetIntegerParam(SolverParameters.IntegerParam.Scaling,
173          (int)(Scaling ? SolverParameters.ScalingValues.ScalingOn : SolverParameters.ScalingValues.ScalingOff));
174
175        if (!solver.SetSolverSpecificParametersAsString(SolverSpecificParameters))
176          throw new ArgumentException("Solver specific parameters could not be set. " +
177            $"For details, see the log files in '{LogDirectory}'.");
178
179        resultStatus = (ResultStatus)solver.Solve(parameters);
180      }
181
182      var objectiveValue = solver.Objective()?.Value();
183
184      problemDefinition.Analyze(solver, results);
185
186      if (solver.IsMip()) {
187        var objectiveBound = solver.Objective()?.BestBound();
188        var absoluteGap = objectiveValue.HasValue && objectiveBound.HasValue
189          ? Math.Abs(objectiveBound.Value - objectiveValue.Value)
190          : (double?)null;
191        // https://www.ibm.com/support/knowledgecenter/SSSA5P_12.7.1/ilog.odms.cplex.help/CPLEX/Parameters/topics/EpGap.html
192        var relativeGap = absoluteGap.HasValue && objectiveValue.HasValue
193          ? absoluteGap.Value / (1e-10 + Math.Abs(objectiveValue.Value))
194          : (double?)null;
195
196        if (resultStatus == ResultStatus.Optimal && absoluteGap.HasValue && !absoluteGap.Value.IsAlmost(0)) {
197          resultStatus = ResultStatus.OptimalWithinTolerance;
198        }
199
200        results.AddOrUpdateResult("BestObjectiveBound", new DoubleValue(objectiveBound ?? double.NaN));
201        results.AddOrUpdateResult("AbsoluteGap", new DoubleValue(absoluteGap ?? double.NaN));
202        results.AddOrUpdateResult("RelativeGap", new PercentValue(relativeGap ?? double.NaN));
203      }
204
205      results.AddOrUpdateResult("ResultStatus", new EnumValue<ResultStatus>(resultStatus));
206      results.AddOrUpdateResult("BestObjectiveValue", new DoubleValue(objectiveValue ?? double.NaN));
207
208      results.AddOrUpdateResult("NumberOfConstraints", new IntValue(solver.NumConstraints()));
209      results.AddOrUpdateResult("NumberOfVariables", new IntValue(solver.NumVariables()));
210
211      if (solver.IsMip() && solver.Nodes() >= 0) {
212        results.AddOrUpdateResult(nameof(solver.Nodes), new DoubleValue(solver.Nodes()));
213      }
214
215      if (solver.Iterations() >= 0) {
216        results.AddOrUpdateResult(nameof(solver.Iterations), new DoubleValue(solver.Iterations()));
217      }
218
219      results.AddOrUpdateResult(nameof(solver.SolverVersion), new StringValue(solver.SolverVersion()));
220    }
221
222    protected virtual Solver CreateSolver(Solver.OptimizationProblemType optimizationProblemType, string libraryName = null) {
223      if (!string.IsNullOrEmpty(libraryName) && !File.Exists(libraryName)) {
224        var paths = new List<string> {
225          Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath)
226        };
227        var path = Environment.GetEnvironmentVariable("PATH");
228        if (path != null)
229          paths.AddRange(path.Split(';'));
230        if (!paths.Any(p => File.Exists(Path.Combine(p, libraryName))))
231          throw new FileNotFoundException($"Could not find library {libraryName} in PATH.", libraryName);
232      }
233
234      try {
235        solver = new Solver(Name, optimizationProblemType, libraryName ?? string.Empty);
236      } catch {
237        solver = null;
238      }
239
240      if (solver == null)
241        throw new InvalidOperationException($"Could not create {optimizationProblemType}. " +
242          $"For details, see the log files in '{LogDirectory}'.");
243
244      solver.SuppressOutput();
245      return solver;
246    }
247  }
248}
Note: See TracBrowser for help on using the repository browser.