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

Last change on this file since 14526 was 14526, checked in by bburlacu, 4 years ago

#2679: Add analyzer for SingleObjectiveGoalSeekingProblem. Add result aggregation in the GoalSeekingOptimizer. Remove unused dataset parameter from the problems.

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