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 |
|
---|
22 | using System;
|
---|
23 | using System.Collections.Generic;
|
---|
24 | using System.Linq;
|
---|
25 | using System.Windows.Forms;
|
---|
26 | using HeuristicLab.Collections;
|
---|
27 | using HeuristicLab.Common;
|
---|
28 | using HeuristicLab.Core;
|
---|
29 | using HeuristicLab.Data;
|
---|
30 | using HeuristicLab.Encodings.RealVectorEncoding;
|
---|
31 | using HeuristicLab.Optimization;
|
---|
32 | using HeuristicLab.Parameters;
|
---|
33 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
34 | using HeuristicLab.Problems.DataAnalysis;
|
---|
35 |
|
---|
36 | namespace HeuristicLab.ProcessParameterOptimization {
|
---|
37 | [Item("Process parameter optimization problem (single-objective)", "Represents a single objective optimization problem which uses configurable regression solutions to evaluate targets from a given dataset.")]
|
---|
38 | [Creatable("Problems")]
|
---|
39 | [StorableClass]
|
---|
40 | public sealed class SingleObjectiveGoalSeekingProblem : SingleObjectiveBasicProblem<RealVectorEncoding>, IGoalSeekingProblem {
|
---|
41 | private const string ModifiableDatasetParameterName = "Dataset";
|
---|
42 | private const string ProblemDataParameterName = "ProblemData";
|
---|
43 | private const string ControllableParametersParameterName = "ControllableParameters";
|
---|
44 | private const string ControllableParameterBoundsParameterName = "ControllableParameterBounds";
|
---|
45 | private const string TargetGoalsParameterName = "TargetGoals";
|
---|
46 | private const string TargetsParameterName = "Targets";
|
---|
47 | private const string ModelCollectionParameterName = "ModelCollection";
|
---|
48 | private const string RowParameterName = "Row";
|
---|
49 | // these parameters are used by the pareto folding analyzer
|
---|
50 | private const string AllowedRangesParameterName = "AllowedRanges";
|
---|
51 | private const string QualitySumCutoffParameterName = "QualitySumCutoff";
|
---|
52 |
|
---|
53 | #region parameters
|
---|
54 | public IValueParameter<RealVector> AllowedRangesParameter {
|
---|
55 | get { return (IValueParameter<RealVector>)Parameters[AllowedRangesParameterName]; }
|
---|
56 | }
|
---|
57 | public IFixedValueParameter<DoubleValue> QualitySumCutoffParameter {
|
---|
58 | get { return (IFixedValueParameter<DoubleValue>)Parameters[QualitySumCutoffParameterName]; }
|
---|
59 | }
|
---|
60 | public IValueParameter<IRegressionProblemData> ProblemDataParameter {
|
---|
61 | get { return (IValueParameter<IRegressionProblemData>)Parameters[ProblemDataParameterName]; }
|
---|
62 | }
|
---|
63 | public IValueParameter<IDataset> ModifiableDatasetParameter {
|
---|
64 | get { return (IValueParameter<IDataset>)Parameters[ModifiableDatasetParameterName]; }
|
---|
65 | }
|
---|
66 | public IValueParameter<CheckedItemList<StringValue>> ControllableParametersParameter {
|
---|
67 | get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[ControllableParametersParameterName]; }
|
---|
68 | }
|
---|
69 | public IValueParameter<DoubleMatrix> ControllableParameterBoundsParameter {
|
---|
70 | get { return (IValueParameter<DoubleMatrix>)Parameters[ControllableParameterBoundsParameterName]; }
|
---|
71 | }
|
---|
72 | public IFixedValueParameter<ItemCollection<IRegressionModel>> ModelCollectionParameter {
|
---|
73 | get { return (IFixedValueParameter<ItemCollection<IRegressionModel>>)Parameters[ModelCollectionParameterName]; }
|
---|
74 | }
|
---|
75 | public IValueParameter<CheckedItemList<StringValue>> TargetsParameter {
|
---|
76 | get { return (IValueParameter<CheckedItemList<StringValue>>)Parameters[TargetsParameterName]; }
|
---|
77 | }
|
---|
78 | public IValueParameter<DoubleMatrix> TargetGoalsParameter {
|
---|
79 | get { return (IValueParameter<DoubleMatrix>)Parameters[TargetGoalsParameterName]; }
|
---|
80 | }
|
---|
81 | public IFixedValueParameter<IntValue> RowParameter {
|
---|
82 | get { return (IFixedValueParameter<IntValue>)Parameters[RowParameterName]; }
|
---|
83 | }
|
---|
84 | #endregion
|
---|
85 |
|
---|
86 | #region parameter properties
|
---|
87 | private IItemCollection<IRegressionModel> ModelCollection {
|
---|
88 | get { return ModelCollectionParameter.Value; }
|
---|
89 | }
|
---|
90 | public DoubleMatrix TargetGoals {
|
---|
91 | get { return TargetGoalsParameter.Value; }
|
---|
92 | set { TargetGoalsParameter.Value = value; }
|
---|
93 | }
|
---|
94 | // the AllowedRanges property is only used by the pareto folding analyzer
|
---|
95 | public RealVector AllowedRanges {
|
---|
96 | get { return AllowedRangesParameter.Value; }
|
---|
97 | set { AllowedRangesParameter.Value = value; }
|
---|
98 | }
|
---|
99 | public double QualitySumCutoff {
|
---|
100 | get { return QualitySumCutoffParameter.Value.Value; }
|
---|
101 | set { QualitySumCutoffParameter.Value.Value = value; }
|
---|
102 | }
|
---|
103 | #endregion
|
---|
104 |
|
---|
105 | #region IProcessParameterOptimizationProblem properties
|
---|
106 | [Storable]
|
---|
107 | private IRegressionProblemData problemData;
|
---|
108 | public IRegressionProblemData ProblemData {
|
---|
109 | get { return problemData; }
|
---|
110 | set {
|
---|
111 | if (value == null || value == problemData) return;
|
---|
112 | var variables = value.Dataset.DoubleVariables.ToList();
|
---|
113 | if (Models.Any()) {
|
---|
114 | var targets = Models.Select(x => x.TargetVariable);
|
---|
115 | var hashset = new HashSet<string>(variables);
|
---|
116 | foreach (var target in targets) {
|
---|
117 | if (!hashset.Contains(target)) {
|
---|
118 | throw new ArgumentException(string.Format("Incompatible problem data. Target \"{0}\" is missing.", target));
|
---|
119 | }
|
---|
120 | }
|
---|
121 | }
|
---|
122 | problemData = value;
|
---|
123 | dataset = new ModifiableDataset(variables, variables.Select(x => new List<double> { ProblemData.Dataset.GetDoubleValue(x, Row) }));
|
---|
124 | ProblemDataParameter.Value = ProblemData;
|
---|
125 | UpdateControllableParameters();
|
---|
126 | UpdateTargetList();
|
---|
127 | }
|
---|
128 | }
|
---|
129 |
|
---|
130 | public int Row {
|
---|
131 | get { return RowParameter.Value.Value; }
|
---|
132 | set { RowParameter.Value.Value = value; }
|
---|
133 | }
|
---|
134 |
|
---|
135 | public IEnumerable<IRegressionModel> Models {
|
---|
136 | get { return ModelCollectionParameter.Value; }
|
---|
137 | }
|
---|
138 |
|
---|
139 | #region targets
|
---|
140 | public IEnumerable<string> ActiveTargets {
|
---|
141 | get { return TargetList.CheckedItems.Select(x => x.Value.Value); }
|
---|
142 | }
|
---|
143 |
|
---|
144 | public IEnumerable<string> Targets {
|
---|
145 | get { return TargetList.Select(x => x.Value); }
|
---|
146 | }
|
---|
147 |
|
---|
148 | public ICheckedItemList<StringValue> TargetList {
|
---|
149 | get { return TargetsParameter.Value; }
|
---|
150 | set { TargetsParameter.Value = (CheckedItemList<StringValue>)value; }
|
---|
151 | }
|
---|
152 | #endregion
|
---|
153 |
|
---|
154 | #region parameters
|
---|
155 | public IEnumerable<string> ActiveParameters {
|
---|
156 | get { return ControllableParameters.CheckedItems.Select(x => x.Value.Value); }
|
---|
157 | }
|
---|
158 |
|
---|
159 | public ICheckedItemList<StringValue> ControllableParameters {
|
---|
160 | get { return ControllableParametersParameter.Value; }
|
---|
161 | set { ControllableParametersParameter.Value = (CheckedItemList<StringValue>)value; }
|
---|
162 | }
|
---|
163 |
|
---|
164 | public DoubleMatrix ControllableParameterBounds {
|
---|
165 | get { return ControllableParameterBoundsParameter.Value; }
|
---|
166 | set { ControllableParameterBoundsParameter.Value = value; }
|
---|
167 | }
|
---|
168 | #endregion
|
---|
169 | #endregion
|
---|
170 |
|
---|
171 | #region IProcessParameterOptimizationProblem methods
|
---|
172 | #region solutions
|
---|
173 | public double GetVariableValue(string variableName) {
|
---|
174 | return dataset.GetDoubleValue(variableName, 0);
|
---|
175 | }
|
---|
176 |
|
---|
177 | public void SetVariableValue(string variableName, double variableValue) {
|
---|
178 | dataset.SetVariableValue(variableValue, variableName, 0); // will throw if the variable is not present in the dataset
|
---|
179 | }
|
---|
180 |
|
---|
181 | public double[] GetEstimatedGoalValues(IEnumerable<double> parameters, bool round = false) {
|
---|
182 | var ds = (ModifiableDataset)dataset.Clone();
|
---|
183 | var estimatedValues = new double[ActiveTargets.Count()];
|
---|
184 | var e1 = parameters.GetEnumerator();
|
---|
185 | var e2 = ActiveParameters.GetEnumerator();
|
---|
186 | while (e1.MoveNext() && e2.MoveNext())
|
---|
187 | ds.SetVariableValue(e1.Current, e2.Current, 0);
|
---|
188 | var rows = new[] { 0 }; // actually just one row
|
---|
189 | int i = 0;
|
---|
190 | var models = ModelCollection.ToList();
|
---|
191 | foreach (var target in TargetList.CheckedItems) {
|
---|
192 | var average = 0d;
|
---|
193 | var count = 0;
|
---|
194 | var targetName = target.Value.Value;
|
---|
195 |
|
---|
196 | foreach (var model in models) {
|
---|
197 | if (targetName != model.TargetVariable)
|
---|
198 | continue;
|
---|
199 | average += model.GetEstimatedValues(ds, rows).Single();
|
---|
200 | count++;
|
---|
201 | }
|
---|
202 | var estimatedValue = average / count;
|
---|
203 | if (round) {
|
---|
204 | var accuracy = GetTargetStepSize(targetName);
|
---|
205 | estimatedValues[i] = RoundToNearestStepMultiple(estimatedValue, accuracy);
|
---|
206 | } else {
|
---|
207 | estimatedValues[i] = estimatedValue;
|
---|
208 | }
|
---|
209 | ++i;
|
---|
210 | }
|
---|
211 | return estimatedValues;
|
---|
212 | }
|
---|
213 |
|
---|
214 | public void AddModel(IRegressionModel model) {
|
---|
215 | var target = model.TargetVariable;
|
---|
216 | CheckIfDatasetContainsTarget(target);
|
---|
217 | ModelCollection.Add(model);
|
---|
218 | OnModelsChanged(this, EventArgs.Empty);
|
---|
219 | }
|
---|
220 |
|
---|
221 | // method which throws an exception that can be caught in the event handler if the check fails
|
---|
222 | private void CheckIfDatasetContainsTarget(string target) {
|
---|
223 | if (dataset.DoubleVariables.All(x => x != target))
|
---|
224 | throw new ArgumentException(string.Format("Model target \"{0}\" does not exist in the dataset.", target));
|
---|
225 | }
|
---|
226 |
|
---|
227 | public void RemoveModel(IRegressionModel model) {
|
---|
228 | ModelCollection.Remove(model);
|
---|
229 | OnModelsChanged(this, EventArgs.Empty);
|
---|
230 | }
|
---|
231 |
|
---|
232 | public event EventHandler ModelsChanged;
|
---|
233 | private void OnModelsChanged(object sender, EventArgs args) {
|
---|
234 | var changed = ModelsChanged;
|
---|
235 | if (changed == null) return;
|
---|
236 | changed(sender, args);
|
---|
237 | }
|
---|
238 | #endregion
|
---|
239 |
|
---|
240 | #region targets
|
---|
241 | public bool GetTargetActive(string target) {
|
---|
242 | var item = TargetList.SingleOrDefault(x => x.Value == target);
|
---|
243 | if (item == null)
|
---|
244 | throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
|
---|
245 | return TargetList.ItemChecked(item);
|
---|
246 | }
|
---|
247 |
|
---|
248 | public void SetTargetActive(string target, bool active) {
|
---|
249 | var item = TargetList.SingleOrDefault(x => x.Value == target);
|
---|
250 | if (item == null)
|
---|
251 | throw new ArgumentException(string.Format("SetTargetActive: Invalid target name {0}", target));
|
---|
252 | TargetList.SetItemCheckedState(item, active);
|
---|
253 | OnTargetsChanged(this, EventArgs.Empty);
|
---|
254 | }
|
---|
255 |
|
---|
256 | public double GetTargetGoal(string target) {
|
---|
257 | if (!Targets.Contains(target))
|
---|
258 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
259 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
260 | return TargetGoals[i, 0];
|
---|
261 | }
|
---|
262 |
|
---|
263 | public void SetTargetGoal(string target, double goal) {
|
---|
264 | if (!Targets.Contains(target))
|
---|
265 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
266 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
267 | TargetGoals[i, 0] = goal;
|
---|
268 | OnTargetsChanged(this, EventArgs.Empty);
|
---|
269 | }
|
---|
270 |
|
---|
271 | public double GetTargetWeight(string target) {
|
---|
272 | if (!Targets.Contains(target))
|
---|
273 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
274 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
275 | return TargetGoals[i, 1];
|
---|
276 | }
|
---|
277 |
|
---|
278 | public void SetTargetWeight(string target, double weight) {
|
---|
279 | if (!Targets.Contains(target))
|
---|
280 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
281 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
282 | TargetGoals[i, 1] = weight;
|
---|
283 | OnTargetsChanged(this, EventArgs.Empty);
|
---|
284 | }
|
---|
285 |
|
---|
286 | public double GetTargetVariance(string target) {
|
---|
287 | if (!Targets.Contains(target))
|
---|
288 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
289 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
290 | return TargetGoals[i, 2];
|
---|
291 | }
|
---|
292 |
|
---|
293 | public void SetTargetVariance(string target, double variance) {
|
---|
294 | if (!Targets.Contains(target))
|
---|
295 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
296 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
297 | TargetGoals[i, 2] = variance;
|
---|
298 | OnTargetsChanged(this, EventArgs.Empty);
|
---|
299 | }
|
---|
300 |
|
---|
301 | public double GetTargetStepSize(string target) {
|
---|
302 | if (!Targets.Contains(target))
|
---|
303 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
304 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
305 | return TargetGoals[i, 3];
|
---|
306 | }
|
---|
307 |
|
---|
308 | public void SetTargetStepSize(string target, double stepSize) {
|
---|
309 | if (!Targets.Contains(target))
|
---|
310 | throw new ArgumentException(string.Format("The target variable name \"{0}\" does not exist in the dataset.", target));
|
---|
311 | int i = TargetGoals.RowNames.TakeWhile(x => x != target).Count();
|
---|
312 | TargetGoals[i, 3] = stepSize;
|
---|
313 | OnTargetsChanged(this, EventArgs.Empty);
|
---|
314 | }
|
---|
315 |
|
---|
316 | public event EventHandler TargetsChanged;
|
---|
317 | private void OnTargetsChanged(object sender, EventArgs args) {
|
---|
318 | var changed = TargetsChanged;
|
---|
319 | if (changed == null) return;
|
---|
320 | changed(sender, args);
|
---|
321 | }
|
---|
322 | #endregion // targets
|
---|
323 |
|
---|
324 | #region process parameters
|
---|
325 | public void SetParameterBounds(string parameterName, double min, double max, double step) {
|
---|
326 | int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameterName).Count();
|
---|
327 | if (i < ControllableParameterBounds.Rows) {
|
---|
328 | ControllableParameterBounds[i, 0] = min;
|
---|
329 | ControllableParameterBounds[i, 1] = max;
|
---|
330 | ControllableParameterBounds[i, 2] = step;
|
---|
331 | UpdateEncoding();
|
---|
332 | OnParametersChanged(this, EventArgs.Empty);
|
---|
333 | } else {
|
---|
334 | throw new ArgumentException(string.Format("SetParameterBounds: Invalid parameter name {0}", parameterName));
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | public double GetParameterStepSize(string parameter) {
|
---|
339 | int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
|
---|
340 | if (i < ControllableParameterBounds.Rows)
|
---|
341 | return ControllableParameterBounds[i, 2];
|
---|
342 | throw new ArgumentException(string.Format("GetParameterStepSize: Invalid parameter name {0}", parameter));
|
---|
343 | }
|
---|
344 |
|
---|
345 | public void SetParameterStepSize(string parameter, double stepSize) {
|
---|
346 | int i = ControllableParameterBounds.RowNames.TakeWhile(x => x != parameter).Count();
|
---|
347 | if (i < ControllableParameterBounds.Rows) {
|
---|
348 | ControllableParameterBounds[i, 2] = stepSize;
|
---|
349 | OnParametersChanged(this, EventArgs.Empty);
|
---|
350 | } else {
|
---|
351 | throw new ArgumentException(string.Format("SetParameterStepSize: Invalid parameter name {0}", parameter));
|
---|
352 | }
|
---|
353 | }
|
---|
354 |
|
---|
355 | public bool GetParameterActive(string parameter) {
|
---|
356 | var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
|
---|
357 | if (item == null)
|
---|
358 | throw new ArgumentException(string.Format("GetParameterActive: Invalid target name {0}", parameter));
|
---|
359 | return ControllableParameters.ItemChecked(item);
|
---|
360 | }
|
---|
361 |
|
---|
362 | public void SetParameterActive(string parameter, bool active) {
|
---|
363 | var item = ControllableParameters.SingleOrDefault(x => x.Value == parameter);
|
---|
364 | if (item == null)
|
---|
365 | throw new ArgumentException(string.Format("SetParameterActive: Invalid target name {0}", parameter));
|
---|
366 | ControllableParameters.SetItemCheckedState(item, active);
|
---|
367 | OnParametersChanged(this, EventArgs.Empty);
|
---|
368 | }
|
---|
369 |
|
---|
370 | /// <summary>
|
---|
371 | /// Returns the parameter bounds (min and max) and the step size for the specified parameter
|
---|
372 | /// </summary>
|
---|
373 | /// <param name="parameterName"></param>
|
---|
374 | /// <returns>A double array containing the values (min, max, step) in this order</returns>
|
---|
375 | public double[] GetParameterBounds(string parameterName) {
|
---|
376 | var index = ControllableParameters.TakeWhile(x => x.Value != parameterName).Count();
|
---|
377 | if (index < ControllableParameters.Count) {
|
---|
378 | var min = ControllableParameterBounds[index, 0];
|
---|
379 | var max = ControllableParameterBounds[index, 1];
|
---|
380 | var step = ControllableParameterBounds[index, 2];
|
---|
381 | return new[] { min, max, step };
|
---|
382 | }
|
---|
383 | throw new ArgumentException(string.Format("GetParameterBounds: Unknown parameter {0}.", parameterName));
|
---|
384 | }
|
---|
385 |
|
---|
386 | public void SetControllableParameters(IEnumerable<string> parameterNames) {
|
---|
387 | ControllableParameters = new CheckedItemList<StringValue>();
|
---|
388 | foreach (var v in parameterNames) {
|
---|
389 | ControllableParameters.Add(new StringValue(v), false);
|
---|
390 | }
|
---|
391 | ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
|
---|
392 | ControllableParameterBounds = new DoubleMatrix(ControllableParameters.Count, 3);
|
---|
393 | ControllableParameterBounds.RowNames = GetControllableParameters();
|
---|
394 | ControllableParameterBounds.ColumnNames = new[] { "Min", "Max", "Step" };
|
---|
395 |
|
---|
396 | for (int i = 0; i < ControllableParameters.Count; ++i) {
|
---|
397 | var itemName = ControllableParameters[i].Value;
|
---|
398 | var values = ProblemData.Dataset.GetReadOnlyDoubleValues(itemName).Where(x => !double.IsNaN(x) && !double.IsInfinity(x)).ToList();
|
---|
399 | if (!values.Any()) continue;
|
---|
400 |
|
---|
401 | // add a 20% margin to allow the optimization algorithm more freedom of exploration
|
---|
402 | ControllableParameterBounds[i, 0] = 0.8 * values.Min(); // min
|
---|
403 | ControllableParameterBounds[i, 1] = 1.2 * values.Max(); // max
|
---|
404 | ControllableParameterBounds[i, 2] = 1e-6; // step
|
---|
405 | }
|
---|
406 | OnParametersChanged(this, EventArgs.Empty);
|
---|
407 | }
|
---|
408 |
|
---|
409 | public IEnumerable<string> GetControllableParameters() {
|
---|
410 | return ControllableParameters.Select(x => x.Value);
|
---|
411 | }
|
---|
412 |
|
---|
413 | public event EventHandler ParametersChanged;
|
---|
414 | private void OnParametersChanged(object sender, EventArgs args) {
|
---|
415 | var changed = ParametersChanged;
|
---|
416 | if (changed == null) return;
|
---|
417 | changed(sender, args);
|
---|
418 | }
|
---|
419 | #endregion // process parameters
|
---|
420 | #endregion // IProcessParameterOprimizationProblem methods
|
---|
421 |
|
---|
422 | #region data members
|
---|
423 | [Storable]
|
---|
424 | private ModifiableDataset dataset; // modifiable dataset
|
---|
425 |
|
---|
426 | public override bool Maximization {
|
---|
427 | get { return true; }
|
---|
428 | }
|
---|
429 | #endregion
|
---|
430 |
|
---|
431 | #region constructors
|
---|
432 | [StorableConstructor]
|
---|
433 | private SingleObjectiveGoalSeekingProblem(bool deserializing) : base(deserializing) { }
|
---|
434 |
|
---|
435 | private SingleObjectiveGoalSeekingProblem(SingleObjectiveGoalSeekingProblem original, Cloner cloner)
|
---|
436 | : base(original, cloner) {
|
---|
437 | this.dataset = cloner.Clone(original.dataset);
|
---|
438 | this.problemData = cloner.Clone(original.problemData);
|
---|
439 |
|
---|
440 | RegisterEvents();
|
---|
441 | }
|
---|
442 |
|
---|
443 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
444 | return new SingleObjectiveGoalSeekingProblem(this, cloner);
|
---|
445 | }
|
---|
446 |
|
---|
447 | [StorableHook(HookType.AfterDeserialization)]
|
---|
448 | private void AfterDeserialization() {
|
---|
449 | // backwards-compatibility
|
---|
450 | if (Parameters.ContainsKey("Models")) {
|
---|
451 | var solutions = ((IFixedValueParameter<ItemCollection<IRegressionSolution>>)Parameters["Models"]).Value;
|
---|
452 | var models = new ItemCollection<IRegressionModel>();
|
---|
453 | foreach (var solution in solutions) {
|
---|
454 | var model = solution.Model;
|
---|
455 | model.TargetVariable = solution.ProblemData.TargetVariable;
|
---|
456 | models.Add(model);
|
---|
457 | }
|
---|
458 | if (Parameters.ContainsKey(ModelCollectionParameterName))
|
---|
459 | Parameters.Remove(ModelCollectionParameterName);
|
---|
460 | Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, models));
|
---|
461 | }
|
---|
462 |
|
---|
463 | RegisterEvents();
|
---|
464 | }
|
---|
465 |
|
---|
466 | public SingleObjectiveGoalSeekingProblem() {
|
---|
467 | Parameters.Add(new ValueParameter<IRegressionProblemData>(ProblemDataParameterName, new RegressionProblemData()));
|
---|
468 | Parameters.Add(new ValueParameter<IDataset>(ModifiableDatasetParameterName, dataset) { Hidden = true });
|
---|
469 | Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(ControllableParametersParameterName));
|
---|
470 | Parameters.Add(new ValueParameter<CheckedItemList<StringValue>>(TargetsParameterName));
|
---|
471 | Parameters.Add(new ValueParameter<DoubleMatrix>(ControllableParameterBoundsParameterName));
|
---|
472 | Parameters.Add(new FixedValueParameter<ItemCollection<IRegressionModel>>(ModelCollectionParameterName, new ItemCollection<IRegressionModel>()));
|
---|
473 | Parameters.Add(new ValueParameter<DoubleMatrix>(TargetGoalsParameterName)); // model target weights
|
---|
474 | Parameters.Add(new FixedValueParameter<IntValue>(RowParameterName));
|
---|
475 |
|
---|
476 | // when the problem is created, the problem data parameter will be set to a default value
|
---|
477 | // set the internal property to the same value
|
---|
478 | ProblemData = ProblemDataParameter.Value;
|
---|
479 |
|
---|
480 | UpdateControllableParameters();
|
---|
481 | UpdateTargetList();
|
---|
482 | RegisterEvents();
|
---|
483 | }
|
---|
484 | #endregion
|
---|
485 |
|
---|
486 | public override double Evaluate(Individual individual, IRandom random) {
|
---|
487 | var vector = individual.RealVector();
|
---|
488 | vector.ElementNames = ActiveParameters;
|
---|
489 |
|
---|
490 | int i = 0;
|
---|
491 | // round vector according to parameter step sizes
|
---|
492 | foreach (var parameter in ControllableParameters.CheckedItems) {
|
---|
493 | var step = ControllableParameterBounds[parameter.Index, 2];
|
---|
494 | vector[i] = RoundToNearestStepMultiple(vector[i], step);
|
---|
495 | ++i;
|
---|
496 | }
|
---|
497 | i = 0;
|
---|
498 | var quality = 0d;
|
---|
499 | var estimatedValues = GetEstimatedGoalValues(vector, round: true);
|
---|
500 | // round target estimated values and calculate qualities
|
---|
501 | foreach (var target in ActiveTargets) {
|
---|
502 | var estimatedValue = estimatedValues[i];
|
---|
503 | var goal = GetTargetGoal(target);
|
---|
504 | var weight = GetTargetWeight(target);
|
---|
505 | var variance = GetTargetVariance(target);
|
---|
506 | quality += weight * Math.Pow(estimatedValue - goal, 2) / variance;
|
---|
507 | ++i;
|
---|
508 | }
|
---|
509 | return quality / i;
|
---|
510 | }
|
---|
511 |
|
---|
512 | #region event handlers
|
---|
513 | private void RegisterEvents() {
|
---|
514 | ProblemDataParameter.ValueChanged += OnProblemDataChanged;
|
---|
515 | ModelCollectionParameter.Value.ItemsAdded += ModelCollection_OnItemsAdded;
|
---|
516 | ModelCollectionParameter.Value.ItemsRemoved += ModelCollection_OnItemsRemoved;
|
---|
517 | RowParameter.Value.ValueChanged += OnRowChanged;
|
---|
518 | ControllableParameters.CheckedItemsChanged += ControllableParameters_OnItemsChanged;
|
---|
519 | ControllableParameterBounds.ItemChanged += ControllableParameterBounds_ItemChanged;
|
---|
520 | }
|
---|
521 |
|
---|
522 | private void OnRowChanged(object o, EventArgs e) {
|
---|
523 | // set variables in the modifiable dataset according to the new row
|
---|
524 | foreach (var v in dataset.DoubleVariables)
|
---|
525 | dataset.SetVariableValue(ProblemData.Dataset.GetDoubleValue(v, Row), v, 0);
|
---|
526 | // set the correct targets
|
---|
527 | UpdateTargetList();
|
---|
528 | }
|
---|
529 |
|
---|
530 | private void OnProblemDataChanged(object o, EventArgs e) {
|
---|
531 | try {
|
---|
532 | ProblemData = ProblemDataParameter.Value;
|
---|
533 | }
|
---|
534 | catch (ArgumentException exception) {
|
---|
535 | MessageBox.Show(exception.Message, "Update Problem Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
---|
536 | ProblemDataParameter.Value = problemData;
|
---|
537 | }
|
---|
538 | }
|
---|
539 |
|
---|
540 | private void ModelCollection_OnItemsAdded(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
|
---|
541 | if (e.Items == null) return;
|
---|
542 |
|
---|
543 | var collection = (IObservableCollection<IRegressionModel>)sender;
|
---|
544 | var newItems = e.Items.ToList();
|
---|
545 |
|
---|
546 | foreach (var model in e.Items) {
|
---|
547 | try {
|
---|
548 | CheckIfDatasetContainsTarget(model.TargetVariable);
|
---|
549 | }
|
---|
550 | catch (ArgumentException exception) {
|
---|
551 | MessageBox.Show(exception.Message, "Add Model", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
---|
552 | newItems.Remove(model);
|
---|
553 | collection.Remove(model);
|
---|
554 | }
|
---|
555 | }
|
---|
556 | UpdateTargetList();
|
---|
557 | OnModelsChanged(this, EventArgs.Empty);
|
---|
558 | }
|
---|
559 |
|
---|
560 | private void ModelCollection_OnItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRegressionModel> e) {
|
---|
561 | if (e.Items == null) return;
|
---|
562 | UpdateTargetList();
|
---|
563 | OnModelsChanged(this, EventArgs.Empty);
|
---|
564 | }
|
---|
565 |
|
---|
566 | private void ControllableParameters_OnItemsChanged(object o, CollectionItemsChangedEventArgs<IndexedItem<StringValue>> e) {
|
---|
567 | UpdateEncoding();
|
---|
568 | }
|
---|
569 |
|
---|
570 | private void ControllableParameterBounds_ItemChanged(object o, EventArgs e) {
|
---|
571 | UpdateEncoding();
|
---|
572 | }
|
---|
573 | #endregion
|
---|
574 |
|
---|
575 | #region helper methods
|
---|
576 | private void UpdateControllableParameters() {
|
---|
577 | if (ProblemData == null) return;
|
---|
578 | SetControllableParameters(ProblemData.Dataset.DoubleVariables);
|
---|
579 | }
|
---|
580 |
|
---|
581 | private void UpdateTargetList() {
|
---|
582 | if (ProblemData == null) return;
|
---|
583 | if (!Models.Any()) {
|
---|
584 | TargetGoals = new DoubleMatrix();
|
---|
585 | TargetList = new CheckedItemList<StringValue>();
|
---|
586 | return;
|
---|
587 | }
|
---|
588 | var targetNames = Models.Select(x => x.TargetVariable).Distinct().ToList();
|
---|
589 | var oldTargetGoals = (DoubleMatrix)TargetGoals.Clone();
|
---|
590 | var oldRowIndices = oldTargetGoals.RowNames.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
|
---|
591 | TargetGoals = new DoubleMatrix(targetNames.Count, 4);
|
---|
592 | TargetGoals.RowNames = targetNames;
|
---|
593 | TargetGoals.ColumnNames = new[] { "Goal", "Weight", "Variance", "Step size" };
|
---|
594 |
|
---|
595 | TargetList = new CheckedItemList<StringValue>();
|
---|
596 | for (int i = 0; i < targetNames.Count; ++i) {
|
---|
597 | TargetList.Add(new StringValue(targetNames[i]), true);
|
---|
598 | int rowIndex;
|
---|
599 | if (oldRowIndices.TryGetValue(targetNames[i], out rowIndex)) {
|
---|
600 | for (int j = 0; j < TargetGoals.Columns; ++j)
|
---|
601 | TargetGoals[i, j] = oldTargetGoals[rowIndex, j];
|
---|
602 | } else {
|
---|
603 | TargetGoals[i, 0] = ProblemData.Dataset.GetDoubleValue(targetNames[i], Row);
|
---|
604 | TargetGoals[i, 1] = 1.0;
|
---|
605 | TargetGoals[i, 2] = ProblemData.Dataset.GetReadOnlyDoubleValues(targetNames[i]).Variance();
|
---|
606 | TargetGoals[i, 3] = 1e-6;
|
---|
607 | }
|
---|
608 | }
|
---|
609 | }
|
---|
610 |
|
---|
611 | private void UpdateEncoding() {
|
---|
612 | var activeParameters = ActiveParameters.ToList();
|
---|
613 | if (Encoding == null)
|
---|
614 | Encoding = new RealVectorEncoding(activeParameters.Count);
|
---|
615 | else
|
---|
616 | Encoding.Length = activeParameters.Count;
|
---|
617 |
|
---|
618 | Encoding.Bounds = new DoubleMatrix(activeParameters.Count, 2); // only two columns: min and max
|
---|
619 | Encoding.Bounds.RowNames = activeParameters;
|
---|
620 | Encoding.Bounds.ColumnNames = new[] { "Min.", "Max." };
|
---|
621 |
|
---|
622 | int i = 0;
|
---|
623 | foreach (var item in ControllableParameters.CheckedItems) {
|
---|
624 | var index = item.Index;
|
---|
625 | Encoding.Bounds[i, 0] = ControllableParameterBounds[index, 0];
|
---|
626 | Encoding.Bounds[i, 1] = ControllableParameterBounds[index, 1];
|
---|
627 | ++i;
|
---|
628 | }
|
---|
629 | }
|
---|
630 | #endregion
|
---|
631 |
|
---|
632 | #region util
|
---|
633 | private static double RoundToNearestStepMultiple(double value, double step) {
|
---|
634 | return step * (long)Math.Round(value / step);
|
---|
635 | }
|
---|
636 | #endregion
|
---|
637 | }
|
---|
638 | }
|
---|