Free cookie consent management tool by TermsFeed Policy Generator

source: branches/EfficientGlobalOptimization/HeuristicLab.Algorithms.EGO/EfficientGlobalOptimizationAlgorithm.cs @ 14741

Last change on this file since 14741 was 14741, checked in by bwerth, 7 years ago

#2745 migrated EGO from Connected Vehicles to its own branch, several modifications/simplifications along the way

File size: 21.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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.Linq;
25using System.Threading;
26using HeuristicLab.Algorithms.CMAEvolutionStrategy;
27using HeuristicLab.Algorithms.DataAnalysis;
28using HeuristicLab.Algorithms.EGO;
29using HeuristicLab.Analysis;
30using HeuristicLab.Common;
31using HeuristicLab.Core;
32using HeuristicLab.Data;
33using HeuristicLab.Encodings.RealVectorEncoding;
34using HeuristicLab.Optimization;
35using HeuristicLab.Parameters;
36using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
37using HeuristicLab.Problems.DataAnalysis;
38using HeuristicLab.Random;
39
40namespace HeuristicLab.Problems.SurrogateProblem {
41  [StorableClass]
42  [Creatable(CreatableAttribute.Categories.Algorithms, Priority = 95)]
43  [Item("EfficientGlobalOptimizationAlgortihm", "Solves a problem by sequentially learning a model, solving a subproblem on the model and evaluating the best found solution for this subproblem.")]
44  public class EfficientGlobalOptimizationAlgorithm : BasicAlgorithm {
45    #region Basic-Alg-Essentials
46    public override bool SupportsPause => true;
47    public override Type ProblemType => typeof(SingleObjectiveBasicProblem<IEncoding>);
48    public new SingleObjectiveBasicProblem<IEncoding> Problem
49    {
50      get { return (SingleObjectiveBasicProblem<IEncoding>)base.Problem; }
51      set { base.Problem = value; }
52    }
53    #endregion
54
55    #region ParameterNames
56    private const string GenerationSizeParameterName = "GenerationSize";
57    private const string InfillCriterionParameterName = "InfillCriterion";
58    private const string InfillOptimizationAlgorithmParameterName = "InfillOptimizationAlgorithm";
59    private const string InfillOptimizationRestartsParameterName = "InfillOptimizationRestarts";
60    private const string InitialEvaluationsParameterName = "Initial Evaluations";
61    private const string MaximumIterationsParameterName = "Maximum Iterations";
62    private const string MaximumRuntimeParameterName = "Maximum Runtime";
63    private const string RegressionAlgorithmParameterName = "RegressionAlgorithm";
64    private const string SeedParameterName = "Seed";
65    private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
66    #endregion
67
68    #region ResultNames
69    private const string BestQualityResultName = "Best Quality";
70    private const string BestSolutionResultName = "Best Solution";
71    private const string EvaluatedSoultionsResultName = "EvaluatedSolutions";
72    private const string IterationsResultName = "Iterations";
73    private const string RegressionSolutionResultName = "Model";
74    private const string QualitiesChartResultName = "Qualities";
75    private const string BestQualitiesRowResultName = "Best Quality";
76    private const string CurrentQualitiesRowResultName = "Current Quality";
77    private const string WorstQualitiesRowResultName = "Worst Quality";
78    #endregion
79
80    #region TransmissionResultNames
81    public const string BestInfillSolutionResultName = "BestInfillSolution";
82    public const string BestInfillQualityResultName = "BestInfillQuality";
83    #endregion
84
85    #region ParameterProperties
86    public IFixedValueParameter<IntValue> GenerationSizeParemeter => Parameters[GenerationSizeParameterName] as IFixedValueParameter<IntValue>;
87    public IConstrainedValueParameter<IInfillCriterion> InfillCriterionParameter => Parameters[InfillCriterionParameterName] as IConstrainedValueParameter<IInfillCriterion>;
88    public IValueParameter<Algorithm> InfillOptimizationAlgorithmParameter => Parameters[InfillOptimizationAlgorithmParameterName] as IValueParameter<Algorithm>;
89    public IFixedValueParameter<IntValue> InfillOptimizationRestartsParemeter => Parameters[InfillOptimizationRestartsParameterName] as IFixedValueParameter<IntValue>;
90    public IFixedValueParameter<IntValue> InitialEvaluationsParameter => Parameters[InitialEvaluationsParameterName] as IFixedValueParameter<IntValue>;
91    public IFixedValueParameter<IntValue> MaximumIterationsParameter => Parameters[MaximumIterationsParameterName] as IFixedValueParameter<IntValue>;
92    public IFixedValueParameter<IntValue> MaximumRuntimeParameter => Parameters[MaximumRuntimeParameterName] as IFixedValueParameter<IntValue>;
93    public IValueParameter<IDataAnalysisAlgorithm<IRegressionProblem>> RegressionAlgorithmParameter => Parameters[RegressionAlgorithmParameterName] as IValueParameter<IDataAnalysisAlgorithm<IRegressionProblem>>;
94    public IFixedValueParameter<IntValue> SeedParameter => Parameters[SeedParameterName] as IFixedValueParameter<IntValue>;
95    public FixedValueParameter<BoolValue> SetSeedRandomlyParameter => Parameters[SetSeedRandomlyParameterName] as FixedValueParameter<BoolValue>;
96    #endregion
97
98    #region Properties
99    public int GenerationSize
100    {
101      get { return GenerationSizeParemeter.Value.Value; }
102    }
103    public IInfillCriterion InfillCriterion
104    {
105      get { return InfillCriterionParameter.Value; }
106    }
107    public Algorithm InfillOptimizationAlgorithm
108    {
109      get { return InfillOptimizationAlgorithmParameter.Value; }
110    }
111    public int InfillOptimizationRestarts
112    {
113      get { return InfillOptimizationRestartsParemeter.Value.Value; }
114    }
115    public int InitialEvaluations
116    {
117      get { return InitialEvaluationsParameter.Value.Value; }
118    }
119    public int MaximumIterations
120    {
121      get { return MaximumIterationsParameter.Value.Value; }
122    }
123    public int MaximumRuntime
124    {
125      get { return MaximumRuntimeParameter.Value.Value; }
126    }
127    public IDataAnalysisAlgorithm<IRegressionProblem> RegressionAlgorithm
128    {
129      get { return RegressionAlgorithmParameter.Value; }
130    }
131    public int Seed
132    {
133      get { return SeedParameter.Value.Value; }
134    }
135    public bool SetSeedRandomly
136    {
137      get { return SetSeedRandomlyParameter.Value.Value; }
138    }
139    #endregion
140
141    #region StorableProperties
142    [Storable]
143    private IRandom Random = new MersenneTwister();
144    [Storable]
145    private List<Tuple<RealVector, double>> Samples;
146    #endregion
147
148    #region ResultsProperties
149    private double ResultsBestQuality
150    {
151      get { return ((DoubleValue)Results[BestQualityResultName].Value).Value; }
152      set { ((DoubleValue)Results[BestQualityResultName].Value).Value = value; }
153    }
154    private RealVector ResultsBestSolution
155    {
156      get { return (RealVector)Results[BestSolutionResultName].Value; }
157      set { Results[BestSolutionResultName].Value = value; }
158    }
159    private int ResultsEvaluations
160    {
161      get { return ((IntValue)Results[EvaluatedSoultionsResultName].Value).Value; }
162      set { ((IntValue)Results[EvaluatedSoultionsResultName].Value).Value = value; }
163    }
164    private int ResultsIterations
165    {
166      get { return ((IntValue)Results[IterationsResultName].Value).Value; }
167      set { ((IntValue)Results[IterationsResultName].Value).Value = value; }
168    }
169    private DataTable ResultsQualities
170    {
171      get { return (DataTable)Results[QualitiesChartResultName].Value; }
172    }
173    private DataRow ResultsQualitiesBest
174    {
175      get { return ResultsQualities.Rows[BestQualitiesRowResultName]; }
176    }
177    private DataRow ResultsQualitiesWorst
178    {
179      get { return ResultsQualities.Rows[WorstQualitiesRowResultName]; }
180    }
181    private DataRow ResultsQualitiesIteration
182    {
183      get { return ResultsQualities.Rows[CurrentQualitiesRowResultName]; }
184    }
185    private IRegressionSolution ResultsModel
186    {
187      get { return (IRegressionSolution)Results[RegressionSolutionResultName].Value; }
188      set { Results[RegressionSolutionResultName].Value = value; }
189    }
190    #endregion
191
192    #region HLConstructors
193    [StorableConstructor]
194    protected EfficientGlobalOptimizationAlgorithm(bool deserializing) : base(deserializing) { }
195    [StorableHook(HookType.AfterDeserialization)]
196    private void AfterDeseialization() {
197      RegisterEventhandlers();
198    }
199    protected EfficientGlobalOptimizationAlgorithm(EfficientGlobalOptimizationAlgorithm original, Cloner cloner)
200      : base(original, cloner) {
201      Random = cloner.Clone(Random);
202      if (original.Samples != null) Samples = original.Samples.Select(x => new Tuple<RealVector, double>(cloner.Clone(x.Item1), x.Item2)).ToList();
203      RegisterEventhandlers();
204    }
205    public override IDeepCloneable Clone(Cloner cloner) { return new EfficientGlobalOptimizationAlgorithm(this, cloner); }
206    public EfficientGlobalOptimizationAlgorithm() {
207      var cmaes = new CMAEvolutionStrategy {
208        MaximumGenerations = 300,
209        PopulationSize = 50
210      };
211      var model = new GaussianProcessRegression {
212        Problem = new RegressionProblem()
213      };
214      model.CovarianceFunctionParameter.Value = new CovarianceRationalQuadraticIso();
215      Parameters.Add(new FixedValueParameter<IntValue>(MaximumIterationsParameterName, "", new IntValue(int.MaxValue)));
216      Parameters.Add(new FixedValueParameter<IntValue>(InitialEvaluationsParameterName, "", new IntValue(10)));
217      Parameters.Add(new FixedValueParameter<IntValue>(MaximumRuntimeParameterName, "The maximum runtime in seconds after which the algorithm stops. Use -1 to specify no limit for the runtime", new IntValue(3600)));
218      Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName, "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
219      Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName, "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
220      Parameters.Add(new ValueParameter<IDataAnalysisAlgorithm<IRegressionProblem>>(RegressionAlgorithmParameterName, "The model used to approximate the problem", model));
221      Parameters.Add(new ValueParameter<Algorithm>(InfillOptimizationAlgorithmParameterName, "The algorithm used to solve the expected improvement subproblem", cmaes));
222      Parameters.Add(new FixedValueParameter<IntValue>(InfillOptimizationRestartsParameterName, "Number of restarts of the SubAlgortihm to avoid local optima", new IntValue(1)));
223      Parameters.Add(new FixedValueParameter<IntValue>(GenerationSizeParameterName, "Number points that are sampled every iteration (stadard EGO: 1)", new IntValue(1)));
224      Parameters.Add(new ConstrainedValueParameter<IInfillCriterion>(InfillCriterionParameterName, "Decision what value should decide the next sample"));
225      InfillCriterionParameter.ValidValues.Add(new ExpectedImprovement());
226      InfillCriterionParameter.ValidValues.Add(new ExpectedQuality());
227      InfillCriterionParameter.ValidValues.Add(new ConfidenceBound());
228      SetInfillProblem();
229      RegisterEventhandlers();
230    }
231    #endregion
232
233    protected override void Initialize(CancellationToken cancellationToken) {
234      base.Initialize(cancellationToken);
235      //encoding
236      var enc = Problem.Encoding as RealVectorEncoding;
237      if (enc == null) throw new ArgumentException("The EGO algorithm can only be applied to RealVectorEncodings");
238
239      //random
240      if (SetSeedRandomly) SeedParameter.Value.Value = new System.Random().Next();
241      Random.Reset(Seed);
242      Samples = new List<Tuple<RealVector, double>>();
243
244      //results
245      Results.Add(new Result(IterationsResultName, new IntValue(0)));
246      Results.Add(new Result(EvaluatedSoultionsResultName, new IntValue(0)));
247      Results.Add(new Result(BestSolutionResultName, new RealVector(1)));
248      Results.Add(new Result(BestQualityResultName, new DoubleValue(Problem.Maximization ? double.MinValue : double.MaxValue)));
249      Results.Add(new Result(RegressionSolutionResultName, typeof(IRegressionSolution)));
250      var table = new DataTable(QualitiesChartResultName);
251      table.Rows.Add(new DataRow(BestQualitiesRowResultName));
252      table.Rows.Add(new DataRow(WorstQualitiesRowResultName));
253      table.Rows.Add(new DataRow(CurrentQualitiesRowResultName));
254      Results.Add(new Result(QualitiesChartResultName, table));
255
256      ResultsQualities.Rows.Add(new DataRow("DEBUG:ModelBuildingIterations"));
257
258      //initial samples
259      var points = EgoUtilities.GetUniformRandomDesign(InitialEvaluations, enc.Length, enc.Bounds, Random);
260      foreach (var t in points) {
261        Samples.Add(Evaluate(t));
262        cancellationToken.ThrowIfCancellationRequested();
263      }
264
265      Analyze();
266    }
267
268    protected override void Run(CancellationToken cancellationToken) {
269      for (ResultsIterations = 0; ResultsIterations < MaximumIterations; ResultsIterations++) {
270        try {
271          ResultsModel = BuildModel();
272          cancellationToken.ThrowIfCancellationRequested();
273          for (var i = 0; i < GenerationSize; i++) {
274            var samplepoint = OptimizeInfillProblem();
275            var sample = Evaluate(samplepoint);
276            Samples.Add(sample);
277            cancellationToken.ThrowIfCancellationRequested();
278          }
279
280        }
281        finally {
282          Analyze();
283        }
284      }
285    }
286
287    #region Eventhandling
288    private void RegisterEventhandlers() {
289      DeregisterEventhandlers();
290      RegressionAlgorithmParameter.ValueChanged += OnModelAlgorithmChanged;
291      InfillOptimizationAlgorithmParameter.ValueChanged += OnInfillOptimizationAlgorithmChanged;
292      InfillOptimizationAlgorithm.ProblemChanged += InfillOptimizationProblemChanged;
293      InfillCriterionParameter.ValueChanged += InfillCriterionChanged;
294
295    }
296    private void DeregisterEventhandlers() {
297      RegressionAlgorithmParameter.ValueChanged -= OnModelAlgorithmChanged;
298      InfillOptimizationAlgorithmParameter.ValueChanged -= OnInfillOptimizationAlgorithmChanged;
299      InfillOptimizationAlgorithm.ProblemChanged -= InfillOptimizationProblemChanged;
300      InfillCriterionParameter.ValueChanged -= InfillCriterionChanged;
301    }
302    private void OnInfillOptimizationAlgorithmChanged(object sender, EventArgs args) {
303      SetInfillProblem();
304      InfillOptimizationAlgorithm.ProblemChanged -= InfillOptimizationProblemChanged;
305      InfillOptimizationAlgorithm.ProblemChanged += InfillOptimizationProblemChanged;
306    }
307    private void InfillOptimizationProblemChanged(object sender, EventArgs e) {
308      InfillOptimizationAlgorithm.ProblemChanged -= InfillOptimizationProblemChanged;
309      SetInfillProblem();
310      InfillOptimizationAlgorithm.ProblemChanged += InfillOptimizationProblemChanged;
311    }
312    private void InfillCriterionChanged(object sender, EventArgs e) {
313      var infillProblem = InfillOptimizationAlgorithm.Problem as InfillProblem;
314      if (infillProblem == null) throw new ArgumentException("InfillOptimizationAlgorithm has no InfillProblem. Troubles with Eventhandling?");
315      infillProblem.InfillCriterion = InfillCriterion;
316    }
317    private void OnModelAlgorithmChanged(object sender, EventArgs args) {
318      RegressionAlgorithm.Problem = new RegressionProblem();
319    }
320    protected override void OnExecutionTimeChanged() {
321      base.OnExecutionTimeChanged();
322      if (CancellationTokenSource == null) return;
323      if (MaximumRuntime == -1) return;
324      if (ExecutionTime.TotalSeconds > MaximumRuntime) CancellationTokenSource.Cancel();
325    }
326    protected override void OnPaused() {
327      base.OnPaused();
328      if (InfillOptimizationAlgorithm.ExecutionState == ExecutionState.Started) InfillOptimizationAlgorithm.Pause();
329      if (RegressionAlgorithm.ExecutionState == ExecutionState.Started) RegressionAlgorithm.Pause();
330
331    }
332    protected override void OnStopped() {
333      base.OnStopped();
334      if (InfillOptimizationAlgorithm.ExecutionState != ExecutionState.Stopped) InfillOptimizationAlgorithm.Stop();
335      if (RegressionAlgorithm.ExecutionState != ExecutionState.Stopped) RegressionAlgorithm.Stop();
336    }
337    protected override void OnProblemChanged() {
338      base.OnProblemChanged();
339      var infillProblem = InfillOptimizationAlgorithm.Problem as InfillProblem;
340      if (infillProblem == null) throw new ArgumentException("InfillOptimizationAlgorithm has no InfillProblem. Troubles with Eventhandling?");
341      infillProblem.Problem = Problem;
342    }
343    #endregion
344
345    #region helpers
346    private void SetInfillProblem() {
347      var infillProblem = new InfillProblem {
348        InfillCriterion = InfillCriterion,
349        Problem = Problem
350      };
351      InfillOptimizationAlgorithm.Problem = infillProblem;
352    }
353    private IRegressionSolution BuildModel() {
354      var dataset = EgoUtilities.GetDataSet(Samples);
355      var problemdata = new RegressionProblemData(dataset, dataset.VariableNames.Where(x => !x.Equals("output")), "output");
356      problemdata.TrainingPartition.Start = 0;
357      problemdata.TrainingPartition.End = Samples.Count;
358      problemdata.TestPartition.Start = Samples.Count;
359      problemdata.TestPartition.End = Samples.Count;
360
361      //train
362      var problem = (RegressionProblem)RegressionAlgorithm.Problem;
363      problem.ProblemDataParameter.Value = problemdata;
364      var i = 0;
365      IRegressionSolution solution = null;
366      double r2 = 0;
367      while ((solution == null || RegressionAlgorithm is GaussianProcessRegression && r2 < 0.95) && i++ < 100) {  //TODO: ask why GP degenerates to NaN so often
368        var results = EgoUtilities.SyncRunSubAlgorithm(RegressionAlgorithm, Random.Next(int.MaxValue));
369        solution = results.Select(x => x.Value).OfType<IRegressionSolution>().SingleOrDefault();
370        r2 = solution?.TrainingRSquared ?? 0;
371      }
372      ResultsQualities.Rows["DEBUG:ModelBuildingIterations"].Values.Add(i);
373      if (solution == null) throw new ArgumentException("The Algorithm did not return a Model");
374      return solution;
375    }
376    private RealVector OptimizeInfillProblem() {
377      //parameterize and check InfillProblem
378      var infillProblem = InfillOptimizationAlgorithm.Problem as InfillProblem;
379      if (infillProblem == null) throw new ArgumentException("InfillOptimizationAlgorithm does not have InfillProblem. Problem with Eventhandling?");
380      if (infillProblem.InfillCriterion != InfillCriterion) throw new ArgumentException("InfillCiriterion for Problem is not correct. Problem with Eventhandling?");
381      if (infillProblem.Problem != Problem) throw new ArgumentException("Expensive real problem is not correctly set in InfillProblem. Problem with Eventhandling?");
382      infillProblem.RegressionSolution = ResultsModel;
383
384      RealVector bestVector = null;
385      var bestValue = infillProblem.Maximization ? double.NegativeInfinity : double.PositiveInfinity;
386
387      for (var i = 0; i < InfillOptimizationRestarts; i++) {
388        //optimize
389        var res = EgoUtilities.SyncRunSubAlgorithm(InfillOptimizationAlgorithm, Random.Next(int.MaxValue));
390
391        //extract results
392        if (!res.ContainsKey(BestInfillSolutionResultName)) throw new ArgumentException("The InfillOptimizationAlgorithm did not return a best solution");
393        var v = res[BestInfillSolutionResultName].Value as RealVector;
394        if (!res.ContainsKey(BestInfillQualityResultName)) throw new ArgumentException("The InfillOptimizationAlgorithm did not return a best quality");
395        var d = res[BestInfillQualityResultName].Value as DoubleValue;
396        if (d == null || v == null) throw new ArgumentException("The InfillOptimizationAlgorithm did not return the expected result types");
397
398        //check for improvement
399        if (infillProblem.Maximization != d.Value > bestValue) continue;
400        bestValue = d.Value;
401        bestVector = v;
402      }
403
404      InfillOptimizationAlgorithm.Runs.Clear();
405      return bestVector;
406    }
407    private Tuple<RealVector, double> Evaluate(RealVector point) {
408      var scope = new Scope();
409      scope.Variables.Add(new Variable(Problem.Encoding.Name, point));
410      var ind = new SingleEncodingIndividual(Problem.Encoding, scope);
411      var q = Problem.Evaluate(ind, Random);
412      return new Tuple<RealVector, double>(point, q);
413    }
414    private void Analyze() {
415      ResultsEvaluations = Samples.Count;
416      var max = Samples.ArgMax(x => x.Item2);
417      var min = Samples.ArgMin(x => x.Item2);
418      var best = Samples[Problem.Maximization ? max : min];
419      ResultsBestQuality = best.Item2;
420      ResultsBestSolution = best.Item1;
421      ResultsQualitiesBest.Values.Add(ResultsBestQuality);
422      ResultsQualitiesIteration.Values.Add(Samples[Samples.Count - 1].Item2);
423      ResultsQualitiesWorst.Values.Add(Samples[Problem.Maximization ? min : max].Item2);
424    }
425    #endregion
426  }
427}
Note: See TracBrowser for help on using the repository browser.