1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022018 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.Common;


26  using HeuristicLab.Core;


27  using HeuristicLab.Data;


28  using HeuristicLab.Parameters;


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


30 


31  namespace HeuristicLab.Operators {


32  [Item("DataReducer", "An operator to reduce values of sub scopes.")]


33  [StorableClass]


34  public sealed class DataReducer : SingleSuccessorOperator {


35  #region Parameter Properties


36  public ScopeTreeLookupParameter<IItem> ParameterToReduce {


37  get { return (ScopeTreeLookupParameter<IItem>)Parameters["ParameterToReduce"]; }


38  }


39  public LookupParameter<IItem> TargetParameter {


40  get { return (LookupParameter<IItem>)Parameters["TargetParameter"]; }


41  }


42  public ValueLookupParameter<ReductionOperation> ReductionOperation {


43  get { return (ValueLookupParameter<ReductionOperation>)Parameters["ReductionOperation"]; }


44  }


45  public ValueLookupParameter<ReductionOperation> TargetOperation {


46  get { return (ValueLookupParameter<ReductionOperation>)Parameters["TargetOperation"]; }


47  }


48  #endregion


49 


50  [StorableConstructor]


51  private DataReducer(bool deserializing) : base(deserializing) { }


52  private DataReducer(DataReducer original, Cloner cloner)


53  : base(original, cloner) {


54  }


55  public DataReducer()


56  : base() {


57  #region Create parameters


58  Parameters.Add(new ScopeTreeLookupParameter<IItem>("ParameterToReduce", "The parameter on which the reduction operation should be applied."));


59  Parameters.Add(new LookupParameter<IItem>("TargetParameter", "The target variable in which the reduced value should be stored."));


60  Parameters.Add(new ValueLookupParameter<ReductionOperation>("ReductionOperation", "The operation which is applied on the parameters to reduce.", new ReductionOperation()));


61  Parameters.Add(new ValueLookupParameter<ReductionOperation>("TargetOperation", "The operation used to apply the reduced value to the target variable.", new ReductionOperation()));


62  #endregion


63  }


64 


65  public override IDeepCloneable Clone(Cloner cloner) {


66  return new DataReducer(this, cloner);


67  }


68 


69  [StorableHook(HookType.AfterDeserialization)]


70  private void AfterDeserialization() {


71  // BackwardsCompatibility3.3


72  var oldReductionOperation = Parameters["ReductionOperation"] as ValueParameter<ReductionOperation>;


73  if (oldReductionOperation != null) {


74  Parameters.Remove("ReductionOperation");


75  Parameters.Add(new ValueLookupParameter<ReductionOperation>("ReductionOperation", "The operation which is applied on the parameters to reduce.", oldReductionOperation.Value));


76  }


77  var oldTargetOperation = Parameters["TargetOperation"] as ValueParameter<ReductionOperation>;


78  if (oldTargetOperation != null) {


79  Parameters.Remove("TargetOperation");


80  Parameters.Add(new ValueLookupParameter<ReductionOperation>("TargetOperation", "The operation used to apply the reduced value to the target variable.", oldTargetOperation.Value));


81  }


82  }


83 


84  public override IOperation Apply() {


85  var values = ParameterToReduce.ActualValue;


86  if (!values.Any()) return base.Apply();


87 


88  if (values.All(x => x is IntValue)) {


89  CalculateResult(values.OfType<IntValue>().Select(x => x.Value), values.First().GetType());


90  } else if (values.All(x => x is DoubleValue)) {


91  CalculateResult(values.OfType<DoubleValue>().Select(x => x.Value), values.First().GetType());


92  } else if (values.All(x => x is TimeSpanValue)) {


93  CalculateResult(values.OfType<TimeSpanValue>().Select(x => x.Value), values.First().GetType());


94  } else if (values.All(x => x is BoolValue)) {


95  CalculateResult(values.OfType<BoolValue>().Select(x => x.Value), values.First().GetType());


96  } else {


97  throw new ArgumentException(string.Format("Type {0} is not supported by the DataReducer.", values.First().GetType()));


98  }


99 


100  return base.Apply();


101  }


102 


103  #region integer reduction


104  private void CalculateResult(IEnumerable<int> values, Type targetType) {


105  int result;


106  switch (ReductionOperation.ActualValue.Value) {


107  case ReductionOperations.Sum:


108  result = values.Sum();


109  break;


110  case ReductionOperations.Product:


111  result = values.Aggregate(1, (x, y) => x * y);


112  break;


113  case ReductionOperations.Count:


114  result = values.Count();


115  break;


116  case ReductionOperations.Min:


117  result = values.Min();


118  break;


119  case ReductionOperations.Max:


120  result = values.Max();


121  break;


122  case ReductionOperations.Avg:


123  result = (int)Math.Round(values.Average());


124  break;


125  case ReductionOperations.Assign:


126  result = values.Last();


127  break;


128  default:


129  throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.ActualValue.Value, targetType));


130  }


131 


132  IntValue target;


133  switch (TargetOperation.ActualValue.Value) {


134  case ReductionOperations.Sum:


135  target = InitializeTarget<IntValue, int>(targetType, 0);


136  target.Value += result;


137  break;


138  case ReductionOperations.Product:


139  target = InitializeTarget<IntValue, int>(targetType, 1);


140  target.Value = target.Value * result;


141  break;


142  case ReductionOperations.Min:


143  target = InitializeTarget<IntValue, int>(targetType, int.MaxValue);


144  target.Value = Math.Min(target.Value, result);


145  break;


146  case ReductionOperations.Max:


147  target = InitializeTarget<IntValue, int>(targetType, int.MinValue);


148  target.Value = Math.Max(target.Value, result);


149  break;


150  case ReductionOperations.Avg:


151  target = InitializeTarget<IntValue, int>(targetType, result);


152  target.Value = (int)Math.Round((target.Value + result) / 2.0);


153  break;


154  case ReductionOperations.Assign:


155  target = InitializeTarget<IntValue, int>(targetType, 0);


156  target.Value = result;


157  break;


158  default:


159  throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.ActualValue.Value, targetType));


160  }


161  }


162  #endregion


163  #region double reduction


164  private void CalculateResult(IEnumerable<double> values, Type targetType) {


165  double result;


166  switch (ReductionOperation.ActualValue.Value) {


167  case ReductionOperations.Sum:


168  result = values.Sum();


169  break;


170  case ReductionOperations.Product:


171  result = values.Aggregate(1.0, (x, y) => x * y);


172  break;


173  case ReductionOperations.Count:


174  result = values.Count();


175  break;


176  case ReductionOperations.Min:


177  result = values.Min();


178  break;


179  case ReductionOperations.Max:


180  result = values.Max();


181  break;


182  case ReductionOperations.Avg:


183  result = values.Average();


184  break;


185  case ReductionOperations.Assign:


186  result = values.Last();


187  break;


188  default:


189  throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.ActualValue.Value, targetType));


190  }


191 


192  DoubleValue target;


193  switch (TargetOperation.ActualValue.Value) {


194  case ReductionOperations.Sum:


195  target = InitializeTarget<DoubleValue, double>(targetType, 0.0);


196  target.Value += result;


197  break;


198  case ReductionOperations.Product:


199  target = InitializeTarget<DoubleValue, double>(targetType, 1.0);


200  target.Value = target.Value * result;


201  break;


202  case ReductionOperations.Min:


203  target = InitializeTarget<DoubleValue, double>(targetType, double.MaxValue);


204  target.Value = Math.Min(target.Value, result);


205  break;


206  case ReductionOperations.Max:


207  target = InitializeTarget<DoubleValue, double>(targetType, double.MinValue);


208  target.Value = Math.Max(target.Value, result);


209  break;


210  case ReductionOperations.Avg:


211  target = InitializeTarget<DoubleValue, double>(targetType, result);


212  target.Value = (target.Value + result) / 2.0;


213  break;


214  case ReductionOperations.Assign:


215  target = InitializeTarget<DoubleValue, double>(targetType, 0.0);


216  target.Value = result;


217  break;


218  default:


219  throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.ActualValue.Value, targetType));


220  }


221  }


222  #endregion


223  #region TimeSpan reduction


224  private void CalculateResult(IEnumerable<TimeSpan> values, Type targetType) {


225  TimeSpan result;


226  switch (ReductionOperation.ActualValue.Value) {


227  case ReductionOperations.Sum:


228  result = values.Aggregate(new TimeSpan(), (x, y) => x + y);


229  break;


230  case ReductionOperations.Min:


231  result = values.Min();


232  break;


233  case ReductionOperations.Max:


234  result = values.Max();


235  break;


236  case ReductionOperations.Avg:


237  result = TimeSpan.FromMilliseconds(values.Average(x => x.TotalMilliseconds));


238  break;


239  case ReductionOperations.Assign:


240  result = values.Last();


241  break;


242  default:


243  throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.ActualValue.Value, targetType));


244  }


245 


246  TimeSpanValue target;


247  switch (TargetOperation.ActualValue.Value) {


248  case ReductionOperations.Sum:


249  target = InitializeTarget<TimeSpanValue, TimeSpan>(targetType, new TimeSpan());


250  target.Value += result;


251  break;


252  case ReductionOperations.Min:


253  target = InitializeTarget<TimeSpanValue, TimeSpan>(targetType, TimeSpan.MaxValue);


254  target.Value = target.Value < result ? target.Value : result;


255  break;


256  case ReductionOperations.Max:


257  target = InitializeTarget<TimeSpanValue, TimeSpan>(targetType, TimeSpan.MinValue);


258  target.Value = target.Value > result ? target.Value : result;


259  break;


260  case ReductionOperations.Avg:


261  target = InitializeTarget<TimeSpanValue, TimeSpan>(targetType, result);


262  target.Value = TimeSpan.FromMilliseconds((target.Value.TotalMilliseconds + result.TotalMilliseconds) / 2);


263  break;


264  case ReductionOperations.Assign:


265  target = InitializeTarget<TimeSpanValue, TimeSpan>(targetType, new TimeSpan());


266  target.Value = result;


267  break;


268  default:


269  throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.ActualValue.Value, targetType));


270  }


271  }


272  #endregion


273  #region bool reduction


274  private void CalculateResult(IEnumerable<bool> values, Type targetType) {


275  bool result;


276  switch (ReductionOperation.ActualValue.Value) {


277  case ReductionOperations.All:


278  result = values.All(x => x);


279  break;


280  case ReductionOperations.Any:


281  result = values.Any(x => x);


282  break;


283  default:


284  throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.ActualValue.Value, targetType));


285  }


286 


287  BoolValue target;


288  switch (TargetOperation.ActualValue.Value) {


289  case ReductionOperations.Assign:


290  target = InitializeTarget<BoolValue, bool>(targetType, true);


291  target.Value = result;


292  break;


293  default:


294  throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.ActualValue.Value, targetType));


295  }


296  }


297  #endregion


298 


299  #region helpers


300  private T1 InitializeTarget<T1, T2>(Type targetType, T2 initialValue)


301  where T1 : ValueTypeValue<T2>


302  where T2 : struct {


303  T1 target = (T1)TargetParameter.ActualValue;


304  if (target == null) {


305  target = (T1)Activator.CreateInstance(targetType);


306  TargetParameter.ActualValue = target;


307  target.Value = initialValue;


308  }


309  return target;


310  }


311  #endregion


312  }


313  }

