Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/MultiObjectiveGoalSeekingProblem.cs @ 14333

Last change on this file since 14333 was 14333, checked in by bburlacu, 8 years ago

#2679: Refactor problems and extract common functionality in static util class.

File size: 13.4 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 HeuristicLab.Collections;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.RealVectorEncoding;
30using HeuristicLab.Optimization;
31using HeuristicLab.Parameters;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
33using HeuristicLab.Problems.DataAnalysis;
34
35namespace HeuristicLab.GoalSeeking {
36  [Item("Goal seeking problem (multi-objective)", "Represents a single objective optimization problem which uses configurable regression models to evaluate targets from a given dataset.")]
37  [Creatable("Problems")]
38  [StorableClass]
39  public sealed class MultiObjectiveGoalSeekingProblem : MultiObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
40    #region parameter names
41    private const string ModifiableDatasetParameterName = "Dataset";
42    private const string InputsParameterName = "Inputs";
43    private const string GoalsParameterName = "Goals";
44    private const string ModelsParameterName = "Models";
45    private const string QualitySumCutoffParameterName = "QualitySumCutoff";
46    #endregion
47
48    #region parameters
49    public IValueParameter<CheckedItemList<InputParameter>> InputsParameter {
50      get { return (IValueParameter<CheckedItemList<InputParameter>>)Parameters[InputsParameterName]; }
51    }
52    public IValueParameter<CheckedItemList<GoalParameter>> GoalsParameter {
53      get { return (IValueParameter<CheckedItemList<GoalParameter>>)Parameters[GoalsParameterName]; }
54    }
55    public IFixedValueParameter<ItemCollection<IRegressionModel>> ModelsParameter {
56      get { return (IFixedValueParameter<ItemCollection<IRegressionModel>>)Parameters[ModelsParameterName]; }
57    }
58    public IFixedValueParameter<DoubleValue> QualitySumCutoffParameter {
59      get { return (IFixedValueParameter<DoubleValue>)Parameters[QualitySumCutoffParameterName]; }
60    }
61    #endregion
62
63    #region IGoalSeekingProblem implementation
64    public IEnumerable<IRegressionModel> Models {
65      get { return ModelsParameter.Value; }
66    }
67
68    public IEnumerable<GoalParameter> Goals {
69      get { return GoalsParameter.Value; }
70    }
71
72    public IEnumerable<InputParameter> Inputs {
73      get { return InputsParameter.Value; }
74    }
75
76    public void AddModel(IRegressionModel model) {
77      var models = ModelsParameter.Value;
78      models.Add(model);
79      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
80    }
81
82    public void RemoveModel(IRegressionModel model) {
83      var models = ModelsParameter.Value;
84      models.Remove(model);
85      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
86    }
87
88    public void Configure(IRegressionProblemData problemData, int row) {
89      GoalSeekingUtil.Configure(Goals, Inputs, problemData, row);
90    }
91
92    public IEnumerable<double> GetEstimatedGoalValues(IEnumerable<double> parameterValues, bool round = false) {
93      var ds = (ModifiableDataset)dataset.Clone();
94      foreach (var parameter in ActiveInputs.Zip(parameterValues, (p, v) => new { Name = p.Name, Value = v })) {
95        ds.SetVariableValue(parameter.Value, parameter.Name, 0);
96      }
97      var rows = new[] { 0 }; // actually just one row
98      var estimatedValues =
99        round ? ActiveGoals.Select(t => RoundToNearestStepMultiple(GetModels(t.Name).Average(m => m.GetEstimatedValues(ds, rows).Single()), t.Step))
100              : ActiveGoals.Select(t => GetModels(t.Name).Average(m => m.GetEstimatedValues(ds, rows).Single()));
101      return estimatedValues;
102    }
103
104    public event EventHandler ModelsChanged;
105    public event EventHandler TargetsChanged;
106    public event EventHandler ParametersChanged;
107    #endregion
108
109    private IEnumerable<GoalParameter> ActiveGoals {
110      get { return Goals.Where(x => x.Active); }
111    }
112    private IEnumerable<InputParameter> ActiveInputs {
113      get { return Inputs.Where(x => x.Active); }
114    }
115    private double QualitySumCutoff {
116      get { return QualitySumCutoffParameter.Value.Value; }
117    }
118
119    [Storable]
120    private ModifiableDataset dataset; // modifiable dataset
121
122    [Storable]
123    private bool[] maximization;
124    public override bool[] Maximization {
125      get { return maximization ?? new bool[] { false }; }
126    }
127
128    public ValueParameter<BoolArray> MaximizationParameter {
129      get { return (ValueParameter<BoolArray>)Parameters["Maximization"]; }
130    }
131
132    #region constructors
133    [StorableConstructor]
134    private MultiObjectiveGoalSeekingProblem(bool deserializing) : base(deserializing) { }
135
136    private MultiObjectiveGoalSeekingProblem(MultiObjectiveGoalSeekingProblem original, Cloner cloner) : base(original, cloner) {
137      this.dataset = cloner.Clone(original.dataset);
138
139      RegisterEvents();
140    }
141
142    public override IDeepCloneable Clone(Cloner cloner) {
143      return new MultiObjectiveGoalSeekingProblem(this, cloner);
144    }
145
146    [StorableHook(HookType.AfterDeserialization)]
147    private void AfterDeserialization() {
148      RegisterEvents();
149    }
150
151    public MultiObjectiveGoalSeekingProblem() {
152      dataset = new ModifiableDataset();
153      Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
154      Parameters.Add(new ValueParameter<CheckedItemList<InputParameter>>(InputsParameterName));
155      Parameters.Add(new ValueParameter<CheckedItemList<GoalParameter>>(GoalsParameterName));
156      Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelsParameterName, new ItemCollection<IRegressionModel>()));
157      Parameters.Add(new FixedValueParameter<DoubleValue>(QualitySumCutoffParameterName, new DoubleValue(0.2)));
158      QualitySumCutoffParameter.Hidden = true;
159      EncodingParameter.Hidden = true;
160      EvaluatorParameter.Hidden = true;
161      SolutionCreatorParameter.Hidden = true;
162      MaximizationParameter.Hidden = true;
163      GoalSeekingUtil.UpdateInputs(InputsParameter.Value, Models, InputParameterChanged);
164      Encoding = GoalSeekingUtil.CreateEncoding(ActiveInputs);
165      GoalSeekingUtil.UpdateTargets(GoalsParameter.Value, Models, GoalParameterChanged);
166      RegisterEvents();
167    }
168    #endregion
169
170    public override double[] Evaluate(Individual individual, IRandom random) {
171      var vector = individual.RealVector();
172      vector.ElementNames = ActiveInputs.Select(x => x.Name);
173      int i = 0;
174      // round vector according to parameter step sizes
175      foreach (var parameter in ActiveInputs) {
176        vector[i] = RoundToNearestStepMultiple(vector[i], parameter.Step);
177        ++i;
178      }
179      var estimatedValues = GetEstimatedGoalValues(vector, round: true);
180      var qualities = ActiveGoals.Zip(estimatedValues, (t, v) => new { Target = t, EstimatedValue = v })
181                                 .Select(x => x.Target.Weight * Math.Pow(x.EstimatedValue - x.Target.Goal, 2) / x.Target.Variance);
182      return qualities.ToArray();
183    }
184
185    #region pareto analyzer
186    public override void Analyze(Individual[] individuals, double[][] qualities, ResultCollection results, IRandom random) {
187      var matrix = FilterFrontsByQualitySum(individuals, qualities, Math.Max(QualitySumCutoff, qualities.Min(x => x.Sum())));
188      const string resultName = "Pareto Front Solutions"; // disclaimer: not really a pareto front
189      if (!results.ContainsKey(resultName)) {
190        results.Add(new Result(resultName, matrix));
191      } else {
192        results[resultName].Value = matrix;
193      }
194      base.Analyze(individuals, qualities, results, random);
195    }
196
197    private DoubleMatrix FilterFrontsByQualitySum(Individual[] individuals, double[][] qualities, double qualitySumCutoff) {
198      var activeParameters = ActiveInputs.ToList();
199      var activeGoals = ActiveGoals.ToList();
200      var filteredModels = new List<double[]>();
201      var rowNames = new List<string>();
202      // build list of column names by combining target and parameter names (with their respective original and estimated values)
203      var columnNames = new List<string> { "Quality Sum" };
204      foreach (var target in activeGoals) {
205        columnNames.Add(target.Name);
206        columnNames.Add(target.Name + " (estimated)");
207      }
208      foreach (var parameter in activeParameters) {
209        columnNames.Add(parameter.Name);
210        columnNames.Add(parameter.Name + " (estimated)");
211        columnNames.Add(parameter.Name + " (deviation)");
212      }
213      // filter models based on their quality sum; remove duplicate models
214      var dec = new DoubleEqualityComparer(); // comparer which uses the IsAlmost method for comparing floating point numbers
215      for (int i = 0; i < individuals.Length; ++i) {
216        var qualitySum = qualities[i].Sum();
217        if (qualitySum > qualitySumCutoff)
218          continue;
219        var vector = individuals[i].RealVector();
220        var estimatedValues = GetEstimatedGoalValues(vector).ToList();
221        var rowValues = new double[columnNames.Count];
222        rowValues[0] = qualitySum;
223        int offset = 1;
224        for (int j = 0; j < activeGoals.Count * 2; j += 2) {
225          int k = j + offset;
226          var goal = activeGoals[j / 2].Goal;
227          rowValues[k] = goal; // original value
228          rowValues[k + 1] = estimatedValues[j / 2]; // estimated value
229        }
230        offset += activeGoals.Count * 2;
231        for (int j = 0; j < activeParameters.Count * 3; j += 3) {
232          int k = j + offset;
233          rowValues[k] = 0; // TODO: figure this out and fix
234          rowValues[k + 1] = vector[j / 3];
235          rowValues[k + 2] = rowValues[k + 1] - rowValues[k];
236        }
237        if (!filteredModels.Any(x => x.SequenceEqual(rowValues, dec))) {
238          rowNames.Add((i + 1).ToString());
239          filteredModels.Add(rowValues);
240        }
241      }
242      var matrix = new DoubleMatrix(filteredModels.Count, columnNames.Count) { RowNames = rowNames, ColumnNames = columnNames, SortableView = true };
243      for (int i = 0; i < filteredModels.Count; ++i) {
244        for (int j = 0; j < filteredModels[i].Length; ++j) {
245          matrix[i, j] = filteredModels[i][j];
246        }
247      }
248      return matrix;
249    }
250    #endregion
251
252    #region event handlers
253    private void RegisterEvents() {
254      ModelsParameter.Value.ItemsAdded += ModelCollection_ItemsChanged;
255      ModelsParameter.Value.ItemsRemoved += ModelCollection_ItemsChanged;
256      GoalsParameter.Value.CheckedItemsChanged += GoalSeekingUtil.Goals_CheckedItemsChanged;
257      InputsParameter.Value.CheckedItemsChanged += GoalSeekingUtil.Inputs_CheckedItemsChanged;
258    }
259
260    private void ModelCollection_ItemsChanged(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
261      if (e.Items == null || !e.Items.Any()) return;
262      GoalSeekingUtil.UpdateInputs(InputsParameter.Value, Models, InputParameterChanged);
263      Encoding = GoalSeekingUtil.CreateEncoding(ActiveInputs);
264      GoalSeekingUtil.UpdateTargets(GoalsParameter.Value, Models, GoalParameterChanged);
265      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
266    }
267
268    private void InputParameterChanged(object sender, EventArgs args) {
269      var inputParameter = (InputParameter)sender;
270      var inputs = InputsParameter.Value;
271      if (inputs.ItemChecked(inputParameter) != inputParameter.Active)
272        inputs.SetItemCheckedState(inputParameter, inputParameter.Active);
273      Encoding = GoalSeekingUtil.CreateEncoding(ActiveInputs);
274    }
275
276    private void GoalParameterChanged(object sender, EventArgs args) {
277      var goalParameter = (GoalParameter)sender;
278      var goals = GoalsParameter.Value;
279      if (goals.ItemChecked(goalParameter) != goalParameter.Active)
280        goals.SetItemCheckedState(goalParameter, goalParameter.Active);
281    }
282    #endregion
283
284    #region helper methods
285    // method which throws an exception that can be caught in the event handler if the check fails
286    private void CheckIfDatasetContainsTarget(string target) {
287      if (dataset.DoubleVariables.All(x => x != target))
288        throw new ArgumentException(string.Format("Model target \"{0}\" does not exist in the dataset.", target));
289    }
290    private static double RoundToNearestStepMultiple(double value, double step) {
291      return step * (long)Math.Round(value / step);
292    }
293    private IEnumerable<IRegressionModel> GetModels(string target) {
294      return Models.Where(x => x.TargetVariable == target);
295    }
296    private class DoubleEqualityComparer : IEqualityComparer<double> {
297      public bool Equals(double x, double y) { return x.IsAlmost(y); }
298      public int GetHashCode(double obj) { return obj.GetHashCode(); }
299    }
300    #endregion
301  }
302}
Note: See TracBrowser for help on using the repository browser.