source: branches/2931_OR-Tools_LP_MIP/HeuristicLab.MathematicalOptimization/3.3/LinearProgramming/Problems/ProgrammableLinearProblemDefinition.cs @ 16582

Last change on this file since 16582 was 16582, checked in by ddorfmei, 7 months ago

#2931: solved the issues found during the review

File size: 5.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2018 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.Linq;
24using System.Reflection;
25using Google.OrTools.LinearSolver;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.ExactOptimization.LinearProgramming.Templates;
29using HeuristicLab.Optimization;
30using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
31using HeuristicLab.Problems.Programmable;
32using HeuristicLab.Scripting;
33
34namespace HeuristicLab.ExactOptimization.LinearProgramming {
35
36  [Item("Programmable Linear Problem Definition (LP, MIP)",
37    "Script that defines the model for a linear/mixed integer programming problem.")]
38  [StorableClass]
39  public sealed class ProgrammableLinearProblemDefinition : Script, ILinearProblemDefinition,
40    IStorableContent {
41    private readonly object compileLock = new object();
42
43    [Storable]
44    private readonly VariableStore variableStore;
45
46    [Storable]
47    private bool codeChanged;
48
49    private volatile ILinearProblemDefinition compiledProblemDefinition;
50
51    public ProgrammableLinearProblemDefinition()
52      : base(ScriptTemplates.CompiledLinearProblemDefinition) {
53      Name = "Programmable Linear Problem Definition";
54      variableStore = new VariableStore();
55    }
56
57    [StorableConstructor]
58    private ProgrammableLinearProblemDefinition(bool deserializing) : base(deserializing) {
59    }
60
61    private ProgrammableLinearProblemDefinition(ProgrammableLinearProblemDefinition original,
62      Cloner cloner) : base(original, cloner) {
63      variableStore = cloner.Clone(original.variableStore);
64      codeChanged = original.codeChanged;
65    }
66
67    public event EventHandler ProblemDefinitionChanged;
68
69    public string Filename { get; set; }
70    public dynamic Instance => compiledProblemDefinition;
71    public VariableStore VariableStore => variableStore;
72
73    private ILinearProblemDefinition CompiledProblemDefinition {
74      get {
75        // double checked locking pattern
76        if (compiledProblemDefinition == null) {
77          lock (compileLock) {
78            if (compiledProblemDefinition == null) {
79              if (codeChanged)
80                throw new ProblemDefinitionScriptException("The code has been changed, but was not recompiled.");
81              Compile(false);
82            }
83          }
84        }
85
86        return compiledProblemDefinition;
87      }
88    }
89
90    public void Analyze(Solver solver, ResultCollection results) => CompiledProblemDefinition.Analyze(solver, results);
91
92    public void BuildModel(Solver solver) => CompiledProblemDefinition.BuildModel(solver);
93
94    public override IDeepCloneable Clone(Cloner cloner) {
95      return new ProgrammableLinearProblemDefinition(this, cloner);
96    }
97
98    public override Assembly Compile() => Compile(true);
99
100    protected override void OnCodeChanged() {
101      base.OnCodeChanged();
102      compiledProblemDefinition = null;
103      codeChanged = true;
104    }
105
106    private Assembly Compile(bool fireChanged) {
107      var assembly = base.Compile();
108      var types = assembly.GetTypes();
109      if (!types.Any(x => typeof(CompiledProblemDefinition).IsAssignableFrom(x)))
110        throw new ProblemDefinitionScriptException("The compiled code doesn't contain a problem definition." + Environment.NewLine +
111                                                   $"The problem definition must be a subclass of {nameof(CompiledProblemDefinition)} and implement {nameof(ILinearProblemDefinition)}.");
112      if (types.Count(x => typeof(CompiledProblemDefinition).IsAssignableFrom(x)) > 1)
113        throw new ProblemDefinitionScriptException("The compiled code contains multiple problem definitions." + Environment.NewLine +
114                                                   $"Only one subclass of {nameof(CompiledProblemDefinition)} is allowed.");
115
116      CompiledProblemDefinition inst;
117      try {
118        inst = (CompiledProblemDefinition)Activator.CreateInstance(types.Single(x =>
119         typeof(CompiledProblemDefinition).IsAssignableFrom(x)));
120      } catch (Exception e) {
121        compiledProblemDefinition = null;
122        throw new ProblemDefinitionScriptException(
123          "Instantiating the problem definition failed." + Environment.NewLine + "Check your default constructor.", e);
124      }
125
126      try {
127        inst.vars = new Variables(VariableStore);
128        inst.Initialize();
129      } catch (Exception e) {
130        compiledProblemDefinition = null;
131        throw new ProblemDefinitionScriptException(
132          "Initializing the problem definition failed." + Environment.NewLine + "Check your Initialize() method.", e);
133      }
134
135      try {
136        compiledProblemDefinition = (ILinearProblemDefinition)inst;
137        if (fireChanged) OnProblemDefinitionChanged();
138      } catch (Exception e) {
139        compiledProblemDefinition = null;
140        throw new ProblemDefinitionScriptException(
141          "Using the problem definition in the problem failed." + Environment.NewLine +
142          "Examine this error message carefully.", e);
143      }
144
145      codeChanged = false;
146      return assembly;
147    }
148
149    private void OnProblemDefinitionChanged() => ProblemDefinitionChanged?.Invoke(this, EventArgs.Empty);
150  }
151}
Note: See TracBrowser for help on using the repository browser.