source: branches/HeuristicLab.GoalSeekingProblem/HeuristicLab.GoalSeekingProblem/3.4/SingleObjectiveGoalSeekingProblem.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: 10.8 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 (single-objective)", "Represents a single objective optimization problem which uses configurable regression solutions to evaluate targets from a given dataset.")]
37  [Creatable("Problems")]
38  [StorableClass]
39  public sealed class SingleObjectiveGoalSeekingProblem : SingleObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
40    #region parameter names
41    private const string InputsParameterName = "Inputs";
42    private const string GoalsParameterName = "Goals";
43    private const string ModelsParameterName = "Models";
44    #endregion
45
46    #region parameters
47    public IValueParameter<CheckedItemList<InputParameter>> InputsParameter {
48      get { return (IValueParameter<CheckedItemList<InputParameter>>)Parameters[InputsParameterName]; }
49    }
50    public IValueParameter<CheckedItemList<GoalParameter>> GoalsParameter {
51      get { return (IValueParameter<CheckedItemList<GoalParameter>>)Parameters[GoalsParameterName]; }
52    }
53    public IFixedValueParameter<ItemList<IRegressionModel>> ModelsParameter {
54      get { return (IFixedValueParameter<ItemList<IRegressionModel>>)Parameters[ModelsParameterName]; }
55    }
56    #endregion
57
58    #region IGoalSeekingProblem implementation
59    public IEnumerable<IRegressionModel> Models {
60      get { return ModelsParameter.Value; }
61    }
62
63    public IEnumerable<GoalParameter> Goals {
64      get { return GoalsParameter.Value; }
65    }
66
67    public IEnumerable<InputParameter> Inputs {
68      get { return InputsParameter.Value; }
69    }
70
71    public void AddModel(IRegressionModel model) {
72      var models = ModelsParameter.Value;
73      models.Add(model);
74      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
75    }
76
77    public void RemoveModel(IRegressionModel model) {
78      var models = ModelsParameter.Value;
79      models.Remove(model);
80      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
81    }
82
83    public void Configure(IRegressionProblemData problemData, int row) {
84      GoalSeekingUtil.Configure(Goals, Inputs, problemData, row);
85    }
86
87    public IEnumerable<double> GetEstimatedGoalValues(IEnumerable<double> parameterValues, bool round = false) {
88      var ds = (ModifiableDataset)dataset.Clone();
89      foreach (var parameter in ActiveInputs.Zip(parameterValues, (p, v) => new { Name = p.Name, Value = v })) {
90        ds.SetVariableValue(parameter.Value, parameter.Name, 0);
91      }
92      var rows = new[] { 0 }; // actually just one row
93      var estimatedValues =
94        round ? ActiveGoals.Select(t => RoundToNearestStepMultiple(GetModels(t.Name).Average(m => m.GetEstimatedValues(ds, rows).Single()), t.Step))
95              : ActiveGoals.Select(t => GetModels(t.Name).Average(m => m.GetEstimatedValues(ds, rows).Single()));
96      return estimatedValues;
97    }
98
99    public event EventHandler ModelsChanged;
100    public event EventHandler TargetsChanged;
101    public event EventHandler ParametersChanged;
102    #endregion
103
104    private IEnumerable<GoalParameter> ActiveGoals {
105      get { return Goals.Where(x => x.Active); }
106    }
107    private IEnumerable<InputParameter> ActiveInputs {
108      get { return Inputs.Where(x => x.Active); }
109    }
110
111    [Storable]
112    private ModifiableDataset dataset; // modifiable dataset
113
114    public override bool Maximization {
115      get { return false; }
116    }
117
118    #region constructors
119    [StorableConstructor]
120    private SingleObjectiveGoalSeekingProblem(bool deserializing) : base(deserializing) { }
121
122    private SingleObjectiveGoalSeekingProblem(SingleObjectiveGoalSeekingProblem original, Cloner cloner) : base(original, cloner) {
123      this.dataset = cloner.Clone(original.dataset);
124      RegisterEvents();
125    }
126
127    public override IDeepCloneable Clone(Cloner cloner) {
128      return new SingleObjectiveGoalSeekingProblem(this, cloner);
129    }
130
131    [StorableHook(HookType.AfterDeserialization)]
132    private void AfterDeserialization() {
133      RegisterEvents();
134    }
135
136    public SingleObjectiveGoalSeekingProblem() {
137      dataset = new ModifiableDataset();
138      Parameters.Add(new ValueParameter<CheckedItemList<InputParameter>>(InputsParameterName));
139      Parameters.Add(new ValueParameter<CheckedItemList<GoalParameter>>(GoalsParameterName));
140      Parameters.Add(new FixedValueParameter<ItemList<IRegressionModel>>(ModelsParameterName, new ItemList<IRegressionModel>()));
141      EncodingParameter.Hidden = true;
142      EvaluatorParameter.Hidden = true;
143      SolutionCreatorParameter.Hidden = true;
144      RegisterEvents();
145    }
146    #endregion
147
148    public override double Evaluate(Individual individual, IRandom random) {
149      var vector = individual.RealVector();
150      vector.ElementNames = ActiveInputs.Select(x => x.Name);
151      int i = 0;
152      // round vector according to parameter step sizes
153      foreach (var parameter in ActiveInputs) {
154        vector[i] = RoundToNearestStepMultiple(vector[i], parameter.Step);
155        ++i;
156      }
157      var estimatedValues = GetEstimatedGoalValues(vector, round: true);
158      var quality = ActiveGoals.Zip(estimatedValues, (t, v) => new { Target = t, EstimatedValue = v })
159                               .Average(x => x.Target.Weight * Math.Pow(x.EstimatedValue - x.Target.Goal, 2) / x.Target.Variance);
160      return quality;
161    }
162
163    public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {
164      var zipped = individuals.Zip(qualities, (ind, qual) => new { Individual = ind, Quality = qual }).OrderBy(x => x.Quality);
165      var best = Maximization ? zipped.Last() : zipped.First();
166      var realVector = best.Individual.RealVector();
167      const string resultName = "Best Solution";
168
169      var columnNames = new List<string>();
170      foreach (var goal in ActiveGoals) {
171        columnNames.Add(goal.Name);
172        columnNames.Add(goal.Name + " (estimated)");
173      }
174      foreach (var input in ActiveInputs) {
175        columnNames.Add(input.Name);
176        columnNames.Add(input.Name + " (estimated)");
177        columnNames.Add(input.Name + " (deviation)");
178      }
179
180      var m = new DoubleMatrix(1, columnNames.Count) { ColumnNames = columnNames };
181      int i = 0;
182
183      var goals = ActiveGoals.Zip(GetEstimatedGoalValues(realVector, round: true),
184        (goal, value) => new { TargetValue = goal.Goal, EstimatedValue = value });
185
186      foreach (var goal in goals) {
187        m[0, i] = goal.TargetValue;
188        m[0, i + 1] = goal.EstimatedValue;
189        i += 2;
190      }
191
192      var inputs = ActiveInputs.Zip(realVector,
193        (input, value) => new { ActualValue = input.Value, EstimatedValue = value });
194
195      foreach (var input in inputs) {
196        m[0, i] = input.ActualValue;
197        m[0, i + 1] = input.EstimatedValue;
198        m[0, i + 2] = m[0, i] - m[0, i + 1];
199        i += 3;
200      }
201
202      if (!results.ContainsKey(resultName)) {
203        results.Add(new Result(resultName, m));
204      } else {
205        results[resultName].Value = m;
206      }
207
208      base.Analyze(individuals, qualities, results, random);
209    }
210
211    #region event handlers
212    private void RegisterEvents() {
213      ModelsParameter.Value.ItemsAdded += ModelCollection_ItemsChanged;
214      ModelsParameter.Value.ItemsRemoved += ModelCollection_ItemsChanged;
215      GoalsParameter.Value.CheckedItemsChanged += GoalSeekingUtil.Goals_CheckedItemsChanged;
216      InputsParameter.Value.CheckedItemsChanged += GoalSeekingUtil.Inputs_CheckedItemsChanged;
217
218      foreach (var input in Inputs)
219        input.Changed += InputParameterChanged;
220
221      foreach (var goal in Goals)
222        goal.Changed += GoalParameterChanged;
223    }
224
225    private void ModelCollection_ItemsChanged(object sender, CollectionItemsChangedEventArgs<IndexedItem<IRegressionModel>> e) {
226      if (e.Items == null || !e.Items.Any()) return;
227      GoalSeekingUtil.UpdateInputs(InputsParameter.Value, Models, InputParameterChanged);
228      GoalSeekingUtil.UpdateEncoding(Encoding, ActiveInputs);
229      dataset = Inputs.Any() ? new ModifiableDataset(Inputs.Select(x => x.Name), Inputs.Select(x => new List<double> { x.Value })) : new ModifiableDataset();
230      GoalSeekingUtil.UpdateTargets(GoalsParameter.Value, Models, GoalParameterChanged);
231      GoalSeekingUtil.RaiseEvent(this, ModelsChanged);
232    }
233
234    private void InputParameterChanged(object sender, EventArgs args) {
235      var inputParameter = (InputParameter)sender;
236      var inputs = InputsParameter.Value;
237      if (inputs.ItemChecked(inputParameter) != inputParameter.Active)
238        inputs.SetItemCheckedState(inputParameter, inputParameter.Active);
239      GoalSeekingUtil.UpdateEncoding(Encoding, ActiveInputs);
240    }
241
242    private void GoalParameterChanged(object sender, EventArgs args) {
243      var goalParameter = (GoalParameter)sender;
244      var goals = GoalsParameter.Value;
245      if (goals.ItemChecked(goalParameter) != goalParameter.Active)
246        goals.SetItemCheckedState(goalParameter, goalParameter.Active);
247    }
248    #endregion
249
250    #region helper methods
251    // method which throws an exception that can be caught in the event handler if the check fails
252    private void CheckIfDatasetContainsTarget(string target) {
253      if (dataset.DoubleVariables.All(x => x != target))
254        throw new ArgumentException(string.Format("Model target \"{0}\" does not exist in the dataset.", target));
255    }
256
257    private IEnumerable<IRegressionModel> GetModels(string target) {
258      return Models.Where(x => x.TargetVariable == target);
259    }
260
261    private static double RoundToNearestStepMultiple(double value, double step) {
262      return step * (long)Math.Round(value / step);
263    }
264    #endregion
265  }
266}
Note: See TracBrowser for help on using the repository browser.