Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.MetaOptimization/HeuristicLab.Problems.MetaOptimization/3.3/Encoding/ParameterConfigurations/ParameterConfiguration.cs @ 5522

Last change on this file since 5522 was 5522, checked in by cneumuel, 13 years ago

#1215

  • implemented population diversity analysis
File size: 19.6 KB
RevLine 
[5112]1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Linq;
5using HeuristicLab.Common;
6using HeuristicLab.Core;
7using HeuristicLab.Data;
8using HeuristicLab.Parameters;
9using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
10using HeuristicLab.PluginInfrastructure;
[5144]11using System.Text;
[5207]12using System.Reflection;
[5231]13using HeuristicLab.Optimization;
[5277]14using HeuristicLab.Collections;
[5313]15using System.Drawing;
[5112]16
17namespace HeuristicLab.Problems.MetaOptimization {
18  [StorableClass]
19  public class ParameterConfiguration : Item, IParameterConfiguration, IStorableContent {
[5207]20    public bool IsOptimizable {
21      get { return true; }
[5112]22    }
23
24    [Storable]
25    public string Filename { get; set; }
26
27    [Storable]
28    protected bool optimize;
29    public bool Optimize {
30      get { return optimize; }
31      set {
32        if (optimize != value) {
33          optimize = value;
34          if (optimize) {
35            PopulateValueConfigurations();
36          } else {
37            this.ValueConfigurations.Clear();
38          }
39          OnOptimizeChanged();
40          OnToStringChanged();
41        }
42      }
43    }
44
45    [Storable]
[5313]46    private Image itemImage;
47    public virtual Image ItemImage {
48      get { return itemImage ?? base.ItemImage; }
49    }
50
51    [Storable]
[5112]52    protected string parameterName;
53    public string ParameterName {
54      get { return parameterName; }
55      set {
56        if (parameterName != value) {
57          parameterName = value;
58          OnToStringChanged();
59        }
60      }
61    }
62
63    [Storable]
64    protected Type parameterDataType;
65    public Type ParameterDataType {
66      get { return this.parameterDataType; }
67    }
68
69    [Storable]
[5207]70    protected Type[] validTypes;
71    public Type[] ValidTypes {
72      get { return validTypes; }
[5112]73      protected set {
[5207]74        if (this.validTypes != value) {
75          this.validTypes = value;
[5112]76        }
77      }
78    }
79
80    [Storable]
81    protected Type valueDataType;
82    public Type ValueDataType {
83      get { return valueDataType; }
84      protected set { this.valueDataType = value; }
85    }
86
87    [Storable]
[5277]88    protected ICheckedValueConfigurationList valueConfigurations;
89    public ICheckedValueConfigurationList ValueConfigurations {
[5112]90      get { return this.valueConfigurations; }
91      protected set {
92        if (this.valueConfigurations != value) {
93          if (this.valueConfigurations != null) DeregisterValueConfigurationEvents();
94          this.valueConfigurations = value;
95          if (this.valueConfigurations != null) RegisterValueConfigurationEvents();
96        }
97      }
98    }
99
100    [Storable]
101    private int actualValueConfigurationIndex = 0;
102    public int ActualValueConfigurationIndex {
103      get { return actualValueConfigurationIndex; }
104      set { actualValueConfigurationIndex = value; }
105    }
106
107    [Storable]
108    protected ConstrainedValue actualValue;
109    public ConstrainedValue ActualValue {
110      get { return actualValue; }
111      set {
112        if (actualValue != value) {
113          DeregisterActualValueEvents();
114          actualValue = value;
115          RegisterActualValueEvents();
116        }
117      }
118    }
119
120    [Storable]
121    protected bool isNullable;
122    public bool IsNullable {
123      get { return isNullable; }
124      protected set {
125        if (this.isNullable != value) {
126          this.isNullable = value;
127        }
128      }
129    }
130
[5267]131    [Storable]
[5231]132    protected IItemSet<IItem> validValues;
133
[5112]134    #region Constructors and Cloning
135    public ParameterConfiguration(string parameterName, IValueParameter valueParameter) {
136      this.ParameterName = parameterName;
137      this.parameterDataType = valueParameter.GetType();
138      this.valueDataType = valueParameter.DataType;
[5231]139      this.validValues = GetValidValues(valueParameter);
[5207]140      this.validTypes = GetValidTypes(valueParameter).ToArray();
[5112]141      this.IsNullable = valueParameter.ItemName.StartsWith("Optional");
[5313]142      this.itemImage = valueParameter.ItemImage;
[5112]143      if (IsNullable) {
[5207]144        validTypes = new List<Type>(validTypes) { typeof(NullValue) }.ToArray();
[5112]145      }
[5313]146      this.ValueConfigurations = new CheckedValueConfigurationList(this.validValues ?? CreateValidValues());
[5267]147      this.ActualValue = new ConstrainedValue(
[5361]148        valueParameter.Value != null ? valueParameter.Value : null, // don't clone here; otherwise settings of a non-optimized ValueParameter will not be synchronized with the ConstrainedValue
[5267]149        valueParameter.DataType,
150        this.validValues != null ? new ItemSet<IItem>(this.validValues) : CreateValidValues(),
151        this.IsNullable);
[5112]152      if (Optimize) {
153        PopulateValueConfigurations();
154      }
155    }
156
157    public ParameterConfiguration() { }
158    [StorableConstructor]
159    protected ParameterConfiguration(bool deserializing) { }
[5207]160    protected ParameterConfiguration(ParameterConfiguration original, Cloner cloner)
161      : base(original, cloner) {
[5112]162      this.parameterName = original.ParameterName;
163      this.parameterDataType = original.parameterDataType;
164      this.valueDataType = original.ValueDataType;
[5231]165      this.validValues = cloner.Clone(original.validValues);
[5207]166      this.validTypes = original.validTypes.ToArray();
[5112]167      this.valueConfigurations = cloner.Clone(original.ValueConfigurations);
168      this.ActualValue = cloner.Clone(original.ActualValue);
169      this.optimize = original.optimize;
170      this.actualValueConfigurationIndex = original.actualValueConfigurationIndex;
171      this.isNullable = original.isNullable;
[5313]172      this.itemImage = original.itemImage;
[5112]173      if (this.valueConfigurations != null) RegisterValueConfigurationEvents();
174    }
175    public override IDeepCloneable Clone(Cloner cloner) {
176      return new ParameterConfiguration(this, cloner);
177    }
178    [StorableHook(HookType.AfterDeserialization)]
179    private void AfterDeserialization() {
180      if (this.valueConfigurations != null) RegisterValueConfigurationEvents();
181    }
182    #endregion
183
184    private void RegisterValueConfigurationEvents() {
[5277]185      this.ValueConfigurations.CheckedItemsChanged += new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_CheckedItemsChanged);
186      this.ValueConfigurations.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_ItemsAdded);
187      this.ValueConfigurations.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_ItemsRemoved);
[5112]188    }
189
190    private void DeregisterValueConfigurationEvents() {
[5277]191      this.ValueConfigurations.CheckedItemsChanged -= new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_CheckedItemsChanged);
192      this.ValueConfigurations.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_ItemsAdded);
193      this.ValueConfigurations.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<IValueConfiguration>>(ValueConfigurations_ItemsRemoved);
[5112]194    }
195
196    private void RegisterActualValueEvents() {
197      if (this.ActualValue != null) this.ActualValue.ToStringChanged += new EventHandler(ActualValue_ToStringChanged);
198    }
199    private void DeregisterActualValueEvents() {
200      if (this.ActualValue != null) this.ActualValue.ToStringChanged -= new EventHandler(ActualValue_ToStringChanged);
201    }
[5361]202   
[5112]203    private void PopulateValueConfigurations() {
[5207]204      foreach (Type t in this.validTypes) {
205        if (t == typeof(NullValue)) {
[5112]206          this.ValueConfigurations.Add(new NullValueConfiguration());
207        } else {
[5267]208          IItem val;
209          if (ActualValue.Value != null && ActualValue.ValueDataType == t) {
210            val = (IItem)ActualValue.Value.Clone(); // use existing value for that type (if available)
211          } else {
212            val = CreateItem(t);
213          }
[5112]214          this.ValueConfigurations.Add(new ValueConfiguration(val, val.GetType()), true);
215        }
216      }
217    }
218
[5337]219    private static IEnumerable<Type> GetValidTypes(IValueParameter parameter) {
[5267]220      // in case of IOperator return empty list, otherwise hundreds of types would be returned. this mostly occurs for Successor which will never be optimized
221      if (parameter.DataType == typeof(IOperator))
222        return new List<Type>();
223
[5337]224      // return only one type for ValueTypeValues<> (Int, Double, Percent, Bool). Otherwise 2 types would be returned for DoubleValue (PercentValue which is derived)
225      if (IsSubclassOfRawGeneric(typeof(ValueTypeValue<>), parameter.DataType))
226        return new List<Type> { parameter.DataType };
227     
[5231]228      if (IsSubclassOfRawGeneric(typeof(OptionalConstrainedValueParameter<>), parameter.GetType())) {
[5337]229        // use existing validvalues if known
[5231]230        var parameterValidValues = (IEnumerable)parameter.GetType().GetProperty("ValidValues").GetValue(parameter, new object[] { });
231        return parameterValidValues.Cast<object>().Select(x => x.GetType());
232      } else {
[5337]233        // otherwise use all assignable types
234        return ApplicationManager.Manager.GetTypes(parameter.DataType, true);
[5231]235      }
[5112]236    }
237
[5231]238    private IItemSet<IItem> GetValidValues(IValueParameter parameter) {
239      if (IsSubclassOfRawGeneric(typeof(OptionalConstrainedValueParameter<>), parameter.GetType())) {
240        var x = (IEnumerable)parameter.GetType().GetProperty("ValidValues").GetValue(parameter, new object[] { });
[5267]241        return new ItemSet<IItem>(x.Cast<IItem>().Select(y => (IItem)y.Clone())); // cloning actually saves memory, because references to event-subscribers are removed
[5231]242      } else {
243        return null;
244      }
245    }
246
247    public IItem CreateItem(Type type) {
248      // no valid values; just instantiate
249      if (validValues == null)
250        return (IItem)Activator.CreateInstance(type);
251
252      if (type == typeof(NullValue))
253        return new NullValue();
254     
255      // copy value from ValidValues; this ensures that previously set ActualNames for a type are kept
256      IItem value = this.validValues.Where(v => v.GetType() == type).SingleOrDefault();
257      if (value != null)
[5267]258        return value;
[5231]259
260      return null;
261    }
262
[5313]263    private IItemSet<IItem> CreateValidValues() {
[5231]264      var validValues = new ItemSet<IItem>();
265      foreach (Type t in this.validTypes) {
[5267]266        try {
267          var val = CreateItem(t);
268          validValues.Add(val);
269        }
270        catch (MissingMethodException) { /* Constructor is missing, don't use those types */ }
[5231]271      }
272      return validValues;
273    }
274
[5112]275    private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
276      while (toCheck != typeof(object)) {
277        var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
278        if (generic == cur) {
279          return true;
280        }
[5337]281        toCheck = toCheck.BaseType; // baseType is null when toCheck is an Interface
282        if (toCheck == null)
283          return false;
[5112]284      }
285      return false;
286    }
287
[5277]288    void ValueConfigurations_CheckedItemsChanged(object sender, Collections.CollectionItemsChangedEventArgs<IndexedItem<IValueConfiguration>> e) {
[5112]289      OnToStringChanged();
290    }
[5277]291    void ValueConfigurations_ItemsRemoved(object sender, Collections.CollectionItemsChangedEventArgs<IndexedItem<IValueConfiguration>> e) {
[5112]292      OnToStringChanged();
293    }
[5277]294    void ValueConfigurations_ItemsAdded(object sender, Collections.CollectionItemsChangedEventArgs<IndexedItem<IValueConfiguration>> e) {
[5112]295      OnToStringChanged();
296    }
297
298    #region INamedItem Properties
299    public virtual string Name {
300      get { return ParameterName; }
301      set { throw new NotSupportedException(); }
302    }
303    public virtual string Description {
304      get { return base.ItemDescription; }
305      set { throw new NotSupportedException(); }
306    }
307    public virtual bool CanChangeDescription {
308      get { return false; }
309    }
310    public virtual bool CanChangeName {
311      get { return false; }
312    }
313    public override string ItemDescription {
314      get { return base.ItemDescription; }
315    }
316    public override string ItemName {
317      get { return base.ItemName; }
318    }
319    #endregion
320
321    #region Events
322    void ActualValue_ToStringChanged(object sender, EventArgs e) {
323      OnToStringChanged();
324    }
325    #endregion
326
327    #region Event Handler
328    public virtual event EventHandler NameChanged;
329    protected virtual void OnNameChanged(object sender, EventArgs e) {
330      var handler = NameChanged;
331      if (handler != null) handler(sender, e);
332    }
333
334    public virtual event EventHandler<CancelEventArgs<string>> NameChanging;
335    protected virtual void OnNameChanging(object sender, CancelEventArgs<string> e) {
336      var handler = NameChanging;
337      if (handler != null) handler(sender, e);
338    }
339
340    public virtual event EventHandler DescriptionChanged;
341    protected virtual void OnDescriptionChanged(object sender, EventArgs e) {
342      var handler = DescriptionChanged;
343      if (handler != null) handler(sender, e);
344    }
[5207]345
[5112]346    public virtual event EventHandler IsOptimizableChanged;
347    private void OnIsOptimizableChanged() {
348      var handler = IsOptimizableChanged;
349      if (handler != null) handler(this, EventArgs.Empty);
350    }
351
352    public virtual event EventHandler OptimizeChanged;
353    protected virtual void OnOptimizeChanged() {
354      var handler = OptimizeChanged;
355      if (handler != null) handler(this, EventArgs.Empty);
356    }
357    #endregion
358
359    public override string ToString() {
360      if (Optimize) {
361        return string.Format("{0}: (Optimize: {1})", ParameterName, ValueConfigurations.CheckedItems.Count());
362      } else {
363        return string.Format("{0}: {1}", ParameterName, ActualValue.Value);
364      }
365    }
366
[5184]367    public string ParameterInfoString {
368      get {
369        StringBuilder sb = new StringBuilder();
370        if (this.Optimize) {
[5359]371          var vc = this.ValueConfigurations[actualValueConfigurationIndex];
[5361]372          if (vc.ActualValue == null || vc.ActualValue.Value == null) {
373            sb.Append(string.Format("{0}: null", parameterName));
374          } else if (IsSubclassOfRawGeneric(typeof(ValueTypeValue<>), vc.ActualValue.Value.GetType())) {
[5359]375            // for int, double, bool use the value directly
[5361]376            sb.Append(string.Format("{0}: {1}", parameterName, this.ActualValue.Value.ToString()));
[5359]377          } else {
378            // for other types use NumberedName (this also uses the Number-Property for otherwise ambiguous ValueConfigurations)
379            sb.Append(string.Format("{0}: {1}", parameterName, vc.NumberedName));
380          }
[5184]381
382          if (this.ActualValue.Value is IParameterizedItem) {
[5277]383            string subParams = this.ValueConfigurations[actualValueConfigurationIndex].ParameterInfoString;
[5184]384            if (!string.IsNullOrEmpty(subParams)) {
385              sb.Append(" (");
386              sb.Append(subParams);
387              sb.Append(")");
388            }
389          }
390        }
391        return sb.ToString();
[5144]392      }
393    }
394
[5112]395    public static IParameterConfiguration Create(IParameterizedNamedItem parent, IParameter parameter) {
396      if (parameter is IValueParameter) {
397        IValueParameter valueParameter = parameter as IValueParameter;
398        return new ParameterConfiguration(parameter.Name, valueParameter);
399      }
400      return null;
401    }
402
403    public void Parameterize(IValueParameter parameter) {
404      if (Optimize) {
405        if (this.ActualValue.Value is IParameterizedItem) {
[5277]406          this.ValueConfigurations[actualValueConfigurationIndex].Parameterize((IParameterizedItem)this.ActualValue.Value);
[5112]407        }
408      }
[5207]409      var clonedValue = this.ActualValue.Value != null ? (IItem)this.ActualValue.Value.Clone() : null;
410      AdaptValidValues(parameter, clonedValue);
411      parameter.Value = clonedValue;
[5112]412    }
413
[5207]414    /// <summary>
415    /// Adds value to the ValidValues of the parameter if they don't contain the value
416    /// </summary>
417    private void AdaptValidValues(IValueParameter parameter, IItem value) {
418      // this requires some tricky reflection, because the type is unknown at runtime so parameter.ValidValues cannot be casted to Collection<?>
419      if (IsSubclassOfRawGeneric(typeof(OptionalConstrainedValueParameter<>), parameter.GetType())) {
420        var validValues = parameter.GetType().GetProperty("ValidValues").GetValue(parameter, new object[] { });
421        Type validValuesType = validValues.GetType();
422
423        var containsMethod = validValuesType.GetMethod("Contains");
424        if (!(bool)containsMethod.Invoke(validValues, new object[] { value })) {
425          var addMethod = validValuesType.GetMethod("Add");
426          addMethod.Invoke(validValues, new object[] { value });
427        }
428      }
429    }
430
[5112]431    public void Randomize(IRandom random) {
432      if (Optimize) {
[5277]433        foreach (var vc in this.ValueConfigurations) {
434          if (this.ValueConfigurations.ItemChecked(vc)) {
435            vc.Randomize(random);
436          }
[5112]437        }
[5277]438        do {
439          actualValueConfigurationIndex = random.Next(ValueConfigurations.Count());
440        } while (!this.ValueConfigurations.ItemChecked(this.ValueConfigurations[actualValueConfigurationIndex]));
441        this.ActualValue = this.ValueConfigurations[actualValueConfigurationIndex].ActualValue;
[5112]442      }
443    }
444
[5277]445    public void Mutate(IRandom random, MutateDelegate mutate, IIntValueManipulator intValueManipulator, IDoubleValueManipulator doubleValueManipulator) {
[5112]446      if (Optimize) {
[5277]447        foreach (var vc in this.ValueConfigurations) {
448          if (this.ValueConfigurations.ItemChecked(vc)) {
449            vc.Mutate(random, mutate, intValueManipulator, doubleValueManipulator);
450          }
[5112]451        }
[5277]452        mutate(random, this, intValueManipulator, doubleValueManipulator);
[5112]453      }
454    }
455
[5277]456    public void Cross(IRandom random, IOptimizable other, CrossDelegate cross, IIntValueCrossover intValueCrossover, IDoubleValueCrossover doubleValueCrossover) {
[5112]457      if (Optimize) {
458        IParameterConfiguration otherPc = (IParameterConfiguration)other;
459        for (int i = 0; i < this.ValueConfigurations.Count; i++) {
[5277]460          if (this.ValueConfigurations.ItemChecked(this.ValueConfigurations[i])) {
461            this.ValueConfigurations[i].Cross(random, otherPc.ValueConfigurations[i], cross, intValueCrossover, doubleValueCrossover);
462          }
[5112]463        }
[5277]464        cross(random, this, other, intValueCrossover, doubleValueCrossover);
[5112]465      }
466    }
[5144]467
468    public void UpdateActualValueIndexToItem(IValueConfiguration vc) {
[5277]469      for (int i = 0; i < this.ValueConfigurations.Count(); i++) {
470        if (this.ValueConfigurations.ElementAt(i) == vc) {
[5144]471          this.actualValueConfigurationIndex = i;
472          return;
473        }
474      }
475    }
476
[5303]477    public List<IOptimizable> GetAllOptimizables() {
478      var list = new List<IOptimizable>();
479      foreach (var vc in ValueConfigurations) {
480        if (vc.Optimize) {
481          list.Add(vc);
482          list.AddRange(vc.GetAllOptimizables());
483        }
484      }
485      return list;
486    }
[5340]487
488    public void CollectOptimizedParameterNames(List<string> parameterNames, string prefix) {
489      foreach (var vc in ValueConfigurations) {
490        if (vc.Optimize) {
491          vc.CollectOptimizedParameterNames(parameterNames, prefix);
492        }
493      }
494    }
[5522]495
496    public double CalculateSimilarity(IOptimizable optimizable) {
497      var other = (IParameterConfiguration)optimizable;
498      if (this.ActualValueConfigurationIndex == other.ActualValueConfigurationIndex) {
499        return this.ValueConfigurations[this.ActualValueConfigurationIndex].CalculateSimilarity(other.ValueConfigurations[other.ActualValueConfigurationIndex]);
500      } else {
501        return 0.0;
502      }
503    }
[5112]504  }
505}
Note: See TracBrowser for help on using the repository browser.