#region License Information
/* HeuristicLab
* Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
namespace HeuristicLab.Operators {
[Item("DataReducer", "An operator to reduce values of sub scopes.")]
[StorableClass]
public sealed class DataReducer : SingleSuccessorOperator {
#region Parameter Properties
public ScopeTreeLookupParameter ParameterToReduce {
get { return (ScopeTreeLookupParameter)Parameters["ParameterToReduce"]; }
}
public LookupParameter TargetParameter {
get { return (LookupParameter)Parameters["TargetParameter"]; }
}
public ValueParameter ReductionOperation {
get { return (ValueParameter)Parameters["ReductionOperation"]; }
}
public ValueParameter TargetOperation {
get { return (ValueParameter)Parameters["TargetOperation"]; }
}
#endregion
[StorableConstructor]
private DataReducer(bool deserializing) : base(deserializing) { }
private DataReducer(DataReducer original, Cloner cloner)
: base(original, cloner) {
}
public DataReducer()
: base() {
#region Create parameters
Parameters.Add(new ScopeTreeLookupParameter("ParameterToReduce", "The parameter on which the reduction operation should be applied."));
Parameters.Add(new LookupParameter("TargetParameter", "The target variable in which the reduced value should be stored."));
Parameters.Add(new ValueParameter("ReductionOperation", "The operation which is applied on the parameters to reduce."));
Parameters.Add(new ValueParameter("TargetOperation", "The operation used to apply the reduced value to the target variable."));
#endregion
}
public override IDeepCloneable Clone(Cloner cloner) {
return new DataReducer(this, cloner);
}
public override IOperation Apply() {
var values = ParameterToReduce.ActualValue;
if (values.Count() > 0) {
if (values.All(x => x.GetType() == typeof(IntValue))) {
List intValues = new List();
values.ForEach(x => intValues.Add((IntValue)x));
CalculateResult(intValues);
} else if (values.All(x => x.GetType() == typeof(DoubleValue))) {
List doubleValues = new List();
values.ForEach(x => doubleValues.Add((DoubleValue)x));
CalculateResult(doubleValues);
} else if (values.All(x => x.GetType() == typeof(TimeSpanValue))) {
List timeSpanValues = new List();
values.ForEach(x => timeSpanValues.Add((TimeSpanValue)x));
CalculateResult(timeSpanValues);
} else {
throw new ArgumentException(string.Format("Type {0} is not supported by the DataReducer.", values.First().GetType()));
}
}
return base.Apply();
}
private void CalculateResult(List values) {
int result = 1;
if (TargetParameter.ActualValue == null) TargetParameter.ActualValue = new IntValue();
IntValue target = (IntValue)TargetParameter.ActualValue;
switch (ReductionOperation.Value.Value) {
case ReductionOperations.Sum:
result = values.Sum(x => x.Value);
break;
case ReductionOperations.Prod:
values.ForEach(x => result *= x.Value);
break;
case ReductionOperations.Avg:
result = (int)Math.Round(values.Average(x => x.Value));
break;
case ReductionOperations.Min:
result = values.Min(x => x.Value);
break;
case ReductionOperations.Max:
result = values.Max(x => x.Value);
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.Value.Value, result.GetType()));
}
switch (TargetOperation.Value.Value) {
case ReductionOperations.Assign:
target.Value = result;
break;
case ReductionOperations.Sum:
target.Value += result;
break;
case ReductionOperations.Prod:
if (target.Value == 0) target.Value = 1;
target.Value *= result;
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.Value.Value, result.GetType()));
}
}
private void CalculateResult(List values) {
double result = 1.0;
if (TargetParameter.ActualValue == null) TargetParameter.ActualValue = new DoubleValue();
DoubleValue target = (DoubleValue)TargetParameter.ActualValue;
switch (ReductionOperation.Value.Value) {
case ReductionOperations.Sum:
result = values.Sum(x => x.Value);
break;
case ReductionOperations.Prod:
values.ForEach(x => result *= x.Value);
break;
case ReductionOperations.Avg:
result = values.Average(x => x.Value);
break;
case ReductionOperations.Min:
result = values.Min(x => x.Value);
break;
case ReductionOperations.Max:
result = values.Max(x => x.Value);
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.Value.Value, result.GetType()));
}
switch (TargetOperation.Value.Value) {
case ReductionOperations.Assign:
target.Value = result;
break;
case ReductionOperations.Sum:
target.Value += result;
break;
case ReductionOperations.Prod:
if (target.Value == 0.0) target.Value = 1.0;
target.Value *= result;
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.Value.Value, result.GetType()));
}
}
private void CalculateResult(List values) {
TimeSpan result = TimeSpan.Zero;
if (TargetParameter.ActualValue == null) TargetParameter.ActualValue = new TimeSpanValue();
TimeSpanValue target = (TimeSpanValue)TargetParameter.ActualValue;
switch (ReductionOperation.Value.Value) {
case ReductionOperations.Sum:
values.ForEach(x => result = result.Add(x.Value));
break;
case ReductionOperations.Avg:
double avg = values.Average(x => x.Value.TotalMilliseconds);
result = TimeSpan.FromMilliseconds(avg);
break;
case ReductionOperations.Min:
result = values.Min(x => x.Value);
break;
case ReductionOperations.Max:
result = values.Max(x => x.Value);
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as ReductionOperation for type: {1}.", ReductionOperation.Value.Value, result.GetType()));
}
switch (TargetOperation.Value.Value) {
case ReductionOperations.Assign:
target.Value = result;
break;
case ReductionOperations.Sum:
target.Value += result;
break;
default:
throw new InvalidOperationException(string.Format("Operation {0} is not supported as TargetOperation for type: {1}.", TargetOperation.Value.Value, result.GetType()));
}
}
}
}