1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022010 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 HeuristicLab.Collections;


26  using HeuristicLab.Common;


27  using HeuristicLab.Core;


28  using HeuristicLab.Data;


29  using HeuristicLab.Parameters;


30  using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;


31 


32  namespace HeuristicLab.Operators {


33  /// <summary>


34  /// Selects one of its branches (if there are any) given a list of relative probabilities.


35  /// </summary>


36  [Item("StochasticMultiBranch", "Selects one of its branches (if there are any) given a list of relative probabilities.")]


37  [StorableClass]


38  public abstract class StochasticMultiBranch<T> : CheckedMultiOperator<T> where T : class, IOperator {


39  /// <summary>


40  /// Should return true if the StochasticMultiOperator should create a new child operation with the selected successor


41  /// or if it should create a new operation. If you need to shield the parameters of the successor you should return true here.


42  /// </summary>


43  protected abstract bool CreateChildOperation { get; }


44 


45  public ValueLookupParameter<DoubleArray> ProbabilitiesParameter {


46  get { return (ValueLookupParameter<DoubleArray>)Parameters["Probabilities"]; }


47  }


48  public ILookupParameter<IRandom> RandomParameter {


49  get { return (ILookupParameter<IRandom>)Parameters["Random"]; }


50  }


51  public ValueParameter<BoolValue> ReportExecutedOperatorParameter {


52  get { return (ValueParameter<BoolValue>)Parameters["ReportExecutedOperator"]; }


53  }


54  public ValueLookupParameter<StringValue> ExecutedOperatorParameter {


55  get { return (ValueLookupParameter<StringValue>)Parameters["ExecutedOperator"]; }


56  }


57 


58  public DoubleArray Probabilities {


59  get { return ProbabilitiesParameter.Value; }


60  set { ProbabilitiesParameter.Value = value; }


61  }


62 


63  [StorableHook(HookType.AfterDeserialization)]


64  private void AfterDeserializationHook() {


65  #region Backwards Compatibility


66  if (!Parameters.ContainsKey("ExecutedOperator")) {


67  Parameters.Add(new ValueLookupParameter<StringValue>("ExecutedOperator", "The operator that was executed."));


68  }


69  if (!Parameters.ContainsKey("ReportExecutedOperator")) {


70  Parameters.Add(new ValueParameter<BoolValue>("ReportExecutedOperator", "Report the executed operator", new BoolValue(false)));


71  }


72  #endregion


73  }


74 


75  [StorableConstructor]


76  protected StochasticMultiBranch(bool deserializing) : base(deserializing) { }


77  protected StochasticMultiBranch(StochasticMultiBranch<T> original, Cloner cloner)


78  : base(original, cloner) {


79  }


80  /// <summary>


81  /// Initializes a new instance of <see cref="StochasticMultiOperator"/> with two parameters


82  /// (<c>Probabilities</c> and <c>Random</c>).


83  /// </summary>


84  public StochasticMultiBranch()


85  : base() {


86  Parameters.Add(new ValueLookupParameter<DoubleArray>("Probabilities", "The array of relative probabilities for each operator.", new DoubleArray()));


87  Parameters.Add(new LookupParameter<IRandom>("Random", "The random number generator to use."));


88  Parameters.Add(new ValueLookupParameter<StringValue>("ExecutedOperator", "The operator that was executed."));


89  Parameters.Add(new ValueParameter<BoolValue>("ReportExecutedOperator", "Report the executed operator", new BoolValue(false)));


90  }


91 


92  protected override void Operators_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<T>> e) {


93  base.Operators_ItemsRemoved(sender, e);


94  if (Probabilities != null && Probabilities.Length > Operators.Count) {


95  List<double> probs = new List<double>(Probabilities.Cast<double>());


96  var sorted = e.Items.OrderByDescending(x => x.Index);


97  foreach (IndexedItem<T> item in sorted)


98  if (probs.Count > item.Index) probs.RemoveAt(item.Index);


99  Probabilities = new DoubleArray(probs.ToArray());


100  }


101  }


102 


103  protected override void Operators_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IndexedItem<T>> e) {


104  base.Operators_ItemsAdded(sender, e);


105  if (Probabilities != null && Probabilities.Length < Operators.Count) {


106  double avg = (Probabilities.Where(x => x > 0).Count() > 0) ? (Probabilities.Where(x => x > 0).Average()) : (1);


107  // add the average of all probabilities in the respective places (the new operators)


108  var added = e.Items.OrderBy(x => x.Index).ToList();


109  int insertCount = 0;


110  DoubleArray probs = new DoubleArray(Operators.Count);


111  for (int i = 0; i < Operators.Count; i++) {


112  if (insertCount < added.Count && i == added[insertCount].Index) {


113  probs[i] = avg;


114  insertCount++;


115  } else if (i  insertCount < Probabilities.Length) {


116  probs[i] = Probabilities[i  insertCount];


117  } else probs[i] = avg;


118  }


119  Probabilities = probs;


120  }


121  }


122 


123  /// <summary>


124  /// Applies an operator of the branches to the current scope with a


125  /// specific probability.


126  /// </summary>


127  /// <exception cref="InvalidOperationException">Thrown when the list of probabilites does not


128  /// match the number of operators, the list of selected operators is empty,


129  /// or all selected operators have zero probabitlity.</exception>


130  /// <returns>A new operation with the operator that was selected followed by the current operator's successor.</returns>


131  public override IOperation Apply() {


132  IRandom random = RandomParameter.ActualValue;


133  DoubleArray probabilities = ProbabilitiesParameter.ActualValue;


134  if (probabilities.Length != Operators.Count) {


135  throw new InvalidOperationException(Name + ": The list of probabilities has to match the number of operators");


136  }


137  IOperator successor = null;


138  var checkedOperators = Operators.CheckedItems;


139  if (checkedOperators.Count() > 0) {


140  // select a random operator from the checked operators


141  double sum = (from indexedItem in checkedOperators select probabilities[indexedItem.Index]).Sum();


142  if (sum == 0) throw new InvalidOperationException(Name + ": All selected operators have zero probability.");


143  double r = random.NextDouble() * sum;


144  sum = 0;


145  foreach (var indexedItem in checkedOperators) {


146  sum += probabilities[indexedItem.Index];


147  if (sum > r) {


148  successor = indexedItem.Value;


149  break;


150  }


151  }


152  }


153  OperationCollection next = new OperationCollection(base.Apply());


154  if (successor != null) {


155  if(ReportExecutedOperatorParameter.Value.Value)


156  ExecutedOperatorParameter.ActualValue = new StringValue(successor.Name);


157 


158  if (CreateChildOperation)


159  next.Insert(0, ExecutionContext.CreateChildOperation(successor));


160  else next.Insert(0, ExecutionContext.CreateOperation(successor));


161  } else {


162  if (ReportExecutedOperatorParameter.Value.Value)


163  ExecutedOperatorParameter.ActualValue = new StringValue("");


164  }


165  return next;


166  }


167  }


168 


169  /// <summary>


170  /// Selects one of its branches (if there are any) given a list of relative probabilities.


171  /// </summary>


172  [Item("StochasticMultiBranch", "Selects one of its branches (if there are any) given a list of relative probabilities.")]


173  [StorableClass]


174  public class StochasticMultiBranch : StochasticMultiBranch<IOperator> {


175  [StorableConstructor]


176  protected StochasticMultiBranch(bool deserializing) : base(deserializing) { }


177  protected StochasticMultiBranch(StochasticMultiBranch original, Cloner cloner)


178  : base(original, cloner) {


179  }


180  public StochasticMultiBranch() { }


181 


182  public override IDeepCloneable Clone(Cloner cloner) {


183  return new StochasticMultiBranch(this, cloner);


184  }


185 


186  protected override bool CreateChildOperation {


187  get { return false; }


188  }


189  }


190  }

