source: branches/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost/3.3/Evaluators/MonolithicEvaluator.cs @ 13069

Last change on this file since 13069 was 13069, checked in by gkronber, 7 years ago

#2499: imported source code for HeuristicLab.BioBoost from private repository with some changes

File size: 18.4 KB
Line 
1using System;
2using System.Collections;
3using System.Threading;
4using GeoAPI.CoordinateSystems;
5using HeuristicLab.BioBoost.ProblemDescription;
6using HeuristicLab.Common;
7using HeuristicLab.Core;
8using HeuristicLab.Data;
9using HeuristicLab.Operators;
10using HeuristicLab.Optimization;
11using HeuristicLab.Parameters;
12using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
13using System.Collections.Generic;
14using System.Diagnostics;
15using System.Globalization;
16using System.Linq;
17using System.Security.Permissions;
18using HeuristicLab.Analysis;
19using HeuristicLab.BioBoost.Data;
20using HeuristicLab.BioBoost.Operators;
21using HeuristicLab.BioBoost.Representation;
22using HeuristicLab.BioBoost.Utils;
23using HeuristicLab.Encodings.IntegerVectorEncoding;
24using HeuristicLab.Encodings.RealVectorEncoding;
25using HeuristicLab.PluginInfrastructure;
26
27namespace HeuristicLab.BioBoost.Evaluators {
28  [StorableClass]
29  public class MonolithicEvaluator : AggregateEvaluator {
30
31    #region ISingleObjectiveEvaluator Members
32    public double Quality {
33      get { return QualityParameter.ActualValue.Value; }
34      set {  QualityParameter.ActualValue = new DoubleValue(value);}
35    }
36    #endregion
37
38    #region IMultiObjectEvaluator Members
39    private DoubleArray Qualities {
40      get { return QualitiesParameter.ActualValue; }
41      set {  QualitiesParameter.ActualValue = value;} }
42
43    #endregion
44
45    public IValueParameter<IntArray> QualityRoundingParameter { get { return (IValueParameter<IntArray>) Parameters["QualityRounding"]; } }
46    public OptionalConstrainedValueParameter<IDiscreteDoubleValueModifier> AlphaValueModifierParameter { get { return (OptionalConstrainedValueParameter<IDiscreteDoubleValueModifier>) Parameters["AlphaValueModifier"]; } }
47    public IValueLookupParameter<DoubleValue> AlphaParameter { get { return (IValueLookupParameter<DoubleValue>) Parameters["Alpha"]; } }
48
49    public IntArray QualityRounding { get { return QualityRoundingParameter.Value;  } }
50    public IDiscreteDoubleValueModifier AlphaValueModifier { get { return AlphaValueModifierParameter.Value; } }
51    public double Alpha { get { return AlphaParameter.ActualValue.Value; } }
52
53    private static object simplificationLock = new object();
54
55    #region Construction & Cloning
56    [StorableConstructor]
57    protected MonolithicEvaluator(bool isDeserializing) : base(isDeserializing) { }
58    protected MonolithicEvaluator(MonolithicEvaluator orig, Cloner cloner) : base(orig, cloner) { }
59    public MonolithicEvaluator() {
60      Parameters.Add(new ValueParameter<IntArray>("QualityRounding", "The rounding cut-off for the quality (used e.g. for multi-object optimizaiton)."));
61      Parameters.Add(new ValueLookupParameter<DoubleValue>("Alpha", "The weight of the first quality parameter", new DoubleValue(0.5)));
62      Parameters.Add(new OptionalConstrainedValueParameter<IDiscreteDoubleValueModifier>("AlphaValueModifier", "An operator to modify the value of alpha over time",
63        new ItemSet<IDiscreteDoubleValueModifier>( ApplicationManager.Manager.GetInstances<IDiscreteDoubleValueModifier>())));
64      foreach (var modifier in AlphaValueModifierParameter.ValidValues) {
65        modifier.StartIndexParameter.Value = new IntValue(0);
66        modifier.EndIndexParameter.ActualName = "MaximumGenerations";
67        modifier.ValueParameter.ActualName = AlphaParameter.ActualName;
68        modifier.IndexParameter.ActualName = "Generations";
69        modifier.StartValueParameter.Value = new DoubleValue(1);
70        modifier.EndValueParameter.Value = new DoubleValue(0.8);
71      }
72      // disable AggregateEvaluator heritage
73      InitializeCostsAndIntermediateResults = false;
74      OperatorGraph.InitialOperator = null;
75      OperatorGraph.Operators.Clear();
76    }
77    public override IDeepCloneable Clone(Cloner cloner) {
78      return new MonolithicEvaluator(this, cloner);
79    }
80
81    [StorableHook(HookType.AfterDeserialization)]
82    private void AfterDeserialization() {
83      if (!Parameters.ContainsKey("QualityRounding"))
84        Parameters.Add(new ValueParameter<IntArray>("QualityRounding", "The rounding cut-off for the quality (used e.g. for multi-object optimizaiton)."));
85      if (!Parameters.ContainsKey("Alpha"))
86        Parameters.Add(new ValueLookupParameter<DoubleValue>("Alpha", "The weight of the first quality parameter", new DoubleValue(0.5)));
87      if (!Parameters.ContainsKey("AlphaValueModifier")) {
88        Parameters.Add(new OptionalConstrainedValueParameter<IDiscreteDoubleValueModifier>("AlphaValueModifier",
89          "An operator to modify the value of alpha over time",
90          new ItemSet<IDiscreteDoubleValueModifier>(
91            ApplicationManager.Manager.GetInstances<IDiscreteDoubleValueModifier>())));
92        foreach (var modifier in AlphaValueModifierParameter.ValidValues) {
93          modifier.StartIndexParameter.Value = new IntValue(0);
94          modifier.EndIndexParameter.ActualName = "MaximumGenerations";
95          modifier.ValueParameter.ActualName = AlphaParameter.ActualName;
96          modifier.IndexParameter.ActualName = "Generations";
97          modifier.StartValueParameter.Value = new DoubleValue(1);
98          modifier.EndValueParameter.Value = new DoubleValue(0.8);
99        }
100      }
101    }
102    #endregion
103
104    public override IOperation Apply() {
105      var data = LoadOrCreateSimpleDataInScope();
106      RealVector utilizations =
107        GetFromScope<RealVector>(LayerDescriptor.Utilizations.NameWithPrefix(data.Echelons[0].FeedstockName));
108      var temp = new Temp(data.N);
109      temp.Amounts = CalculateFeedstockCosts(data, utilizations, temp);
110      for (int i = 0; i < data.Echelons.Count - 1; i++) {
111        IntegerVector transportTargets =
112          GetFromScope<IntegerVector>(LayerDescriptor.TransportTargets.NameWithPrefix(data.Echelons[i].FeedstockName));
113        temp.Amounts = CalculateLogisticCosts(data, i, temp, transportTargets);
114        CalculateConversionCosts(data, i, temp);
115      }
116      var totalAmount = temp.Amounts.Sum();
117      var maxPossibleAmount = data.Potentials.Sum();
118      for (int i = 0; i < data.Echelons.Count - 1; i++)
119        maxPossibleAmount *= data.Echelons[i].Conversion.MainProductConversionRate;
120      //var nrOfPlants = temp.Amounts.Count(a => a > 0);
121      var revenue = totalAmount*data.FinalProudctPrice;
122      var roi = temp.TotalCost == 0 ? revenue : (revenue-temp.TotalCost)/temp.TotalCost;
123      Quality = Alpha*(roi - temp.TotalPenalty) + (1-Alpha)*totalAmount/maxPossibleAmount;
124      Qualities = new DoubleArray(new[] {Quality, totalAmount});
125      //Qualities = new DoubleArray(new[] {Quality, Math.Log(nrOfPlants)/Math.Log(2)});
126      if (QualityRoundingParameter.Value != null) {
127        if (QualityRounding.Length > 0) {
128          Quality = Math.Round(Quality, QualityRounding[0]);
129          if (QualityRounding.Length >= Qualities.Length) {
130            for (int i = 0; i < Qualities.Length; i++) {
131              int precision = QualityRounding[i];
132              if (precision >= 0) {
133                Qualities[i] = Math.Round(Qualities[i], QualityRounding[i]);
134              } else {
135                var multiplier = Math.Pow(10, Math.Abs(precision));
136                Qualities[i] = Math.Round(Qualities[i]/multiplier)*multiplier;
137              }
138            }
139          }
140        }
141      }
142      var ops = new OperationCollection();
143      if (AlphaValueModifierParameter.ActualValue != null)
144        ops.Add(ExecutionContext.CreateChildOperation(AlphaValueModifier));
145      ops.Add(base.Apply());
146      return ops;
147    }
148
149    private SimpleProblemData LoadOrCreateSimpleDataInScope() {
150      var globalScope = ExecutionContext.Scope;
151      while (globalScope.Parent != null)
152        globalScope = globalScope.Parent;
153      SimpleProblemData data;
154      lock (simplificationLock) {
155        IVariable dataVariable;
156        if (!globalScope.Variables.TryGetValue("SimpleProblemData", out dataVariable) ||
157            !(dataVariable.Value is SimpleProblemData) ||
158             ((SimpleProblemData)dataVariable.Value).Echelons == null) {
159          data = new SimpleProblemData(ProblemData);
160          globalScope.Variables.Remove("SimpleProblemData");
161          globalScope.Variables.Add(new Variable("SimpleProblemData", data));
162        } else {
163          data = dataVariable.Value as SimpleProblemData;
164        }
165      }
166      return data;
167    }
168
169    private double[] CalculateFeedstockCosts(SimpleProblemData data, RealVector utilizations, Temp temp) {
170      double cost = 0;
171      var amountsAtSource = new double[data.N];
172      for (int i = 0; i < data.N; i++) {
173        var amount = data.Potentials[i]*utilizations[i];
174        if (amount < 100) amount = 0; // TODO: make this configurable
175        amountsAtSource[i] = amount;
176        cost += FeedstockCostEvaluator.GetScaledCost(data.FeedstockBasePrices[i], data.FeedstockMaxPrices[i], amount, utilizations[i]);
177      }
178      temp.TotalCost += cost;
179      return amountsAtSource;
180    }
181
182    private double[] CalculateLogisticCosts(SimpleProblemData data, int echelonNr, Temp temp, IntegerVector transportTargets) {
183      var echelon = data.Echelons[echelonNr];
184      var distanceMatrix = echelon.DistanceMatrix;
185      var logistic = echelon.Logistic;
186      double cost = 0;
187      double penalty = 0;
188      double[] amountsAtTarget = new double[data.N];
189      for (int i = 0; i < data.N; i++) {
190        if (temp.Amounts[i] <= 0) continue;
191        var amount = Brace(temp.Amounts[i], logistic.MinAmount, logistic.MaxAmount);
192        var target = transportTargets[i];
193        amountsAtTarget[target] += Math.Min(temp.Amounts[i], amount); // cannot have more than before
194        var distance = distanceMatrix[i, target];
195        if (distance > logistic.MaxDistance)
196          penalty += LogisticCostEvaluator.Penalty(distance, logistic.MaxDistance, distanceMatrix.Max);
197        cost += data.GetLogisticActionCost(logistic.Transport, amount*distance*(1-logistic.WaterContent), i, target);
198        cost += data.GetLogisticActionCost(logistic.Handling, amount*(1-logistic.WaterContent), i, target);
199      }
200      temp.TotalCost += cost;
201      temp.TotalPenalty += penalty;
202      return amountsAtTarget;
203    }
204
205    private static double Brace(double value, double min, double max) { return Math.Min(max, Math.Max(min, value)); }
206
207
208    private void CalculateConversionCosts(SimpleProblemData data, int echelonNr, Temp temp) {
209      var echelon = data.Echelons[echelonNr];
210      var conversion = echelon.Conversion;
211      double cost = 0;
212      double penalty = 0;
213      for (int i = 0; i < data.N; i++) {
214        var amount = temp.Amounts[i];
215        if (amount <= 0) continue;
216        var maxCapacity = echelon.MaxConversionCapacities[i];
217        amount = amount * (1-conversion.DryMatterLoss);
218        var capacity = amount/conversion.UtilizationFactor;
219        // NOTE: available capacities are ignored (unused anyway)
220        var scale = capacity/conversion.DesignCapacity;
221        var minScale = conversion.MinCapacity/conversion.DesignCapacity;
222        var maxScale = maxCapacity/conversion.DesignCapacity;
223        penalty += ConversionCostEvaluator.ScalingPenalty(scale, minScale, maxScale);
224        penalty += ConversionCostEvaluator.CapacityPenalty(capacity, maxCapacity);
225        cost += ConversionCostEvaluator.ScaledCost(scale, maxScale, conversion.Construction, conversion.ConstructionScalingExponent);
226        cost += ConversionCostEvaluator.ScaledCost(scale, maxScale, conversion.Maintenance, conversion.MaintenanceScalingExponent);
227        cost += conversion.Cost*amount; // side product costs and revenues have been factored in before
228        var storageCapacity = capacity/365*conversion.SafetyStock;
229        cost += data.GetLogisticActionCost(conversion.Storage, storageCapacity, i);
230        temp.Amounts[i] = amount*conversion.MainProductConversionRate;
231      }
232      temp.TotalCost += cost;
233      temp.TotalPenalty += penalty;
234    }
235
236    private T GetFromScope<T>(string variableName) where T: class{
237      var variables = ExecutionContext.Scope.Variables;
238      return variables[variableName].Value as T;
239    }
240  }
241
242  public sealed class Temp {
243    public double[] Amounts;
244    public double TotalCost;
245    public double TotalPenalty;
246    public Temp(int n) { Amounts = new double[n]; }
247  }
248
249  [StorableClass(StorableClassType.AllFieldsAndAllProperties)]
250  public sealed class SimpleProblemData : Item {
251
252    [StorableConstructor]
253    private SimpleProblemData(bool isDeserializing) : base(isDeserializing) { }
254
255    private SimpleProblemData(SimpleProblemData orig, Cloner cloner) : base(orig, cloner) {
256      FinalProudctPrice = orig.FinalProudctPrice;
257      N = orig.N;
258      Potentials = Util.Clone(orig.Potentials);
259      FeedstockBasePrices = Util.Clone(orig.FeedstockBasePrices);
260      FeedstockMaxPrices = Util.Clone(orig.FeedstockMaxPrices);
261      invest = Util.Clone(orig.invest);
262      fuel = Util.Clone(orig.fuel);
263      other = Util.Clone(orig.other);
264      Echelons = orig.Echelons.Select(cloner.Clone).ToList();
265    }
266
267    public SimpleProblemData(BioBoostProblemData data) {
268      N = data.LocationNames.Length;
269      var feedstockName = data.Utilizations.CheckedItems.First().Value.Value;
270      Potentials = ((IValueParameter<DoubleArray>) data.FeedstockPotentials[LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(feedstockName)]).Value.ToArray();
271      var feedstock = data.Products.First(p => p.Label == feedstockName);
272      FeedstockBasePrices = data.LocationNames.Select(loc => feedstock.GetRegionalBasePrice(loc)).ToArray();
273      FeedstockMaxPrices = data.LocationNames.Select(loc => feedstock.GetRegionalMaxPrice(loc)).ToArray();
274      invest = data.InvestmentCostFactors.ToArray();
275      maint = data.MaintenanceCostFactors.ToArray();
276      fuel = data.FuelCostFactors.ToArray();
277      labor = data.LaborCostFactors.ToArray();
278      other = data.OtherCostFactors.ToArray();
279      var transportedProducts = data.TransportTargets.CheckedItems.Select(i => i.Value.Value).ToArray();
280      var prices = data.Products.ToDictionary(p => p.Label, p => p.Price);
281        // different prices (@source/@target) are not considered (unused anyway)
282      Echelons = new List<Echelon>(3);
283      for (int i = 0; i < transportedProducts.Length; i++) {
284        var echelon = new Echelon();
285        var productName = echelon.FeedstockName = transportedProducts[i];
286        var logistic = echelon.Logistic = (Logistic) data.Logistics[productName].First().Clone(); // TODO: make better choice
287        logistic.Transport.Other += logistic.Transport.Emissions.TotalCost(prices, null);
288        logistic.Handling.Other += logistic.Handling.Emissions.TotalCost(prices, null);
289        echelon.DistanceMatrix = GetFromProblemData<DistanceMatrix>(data, logistic.Distances + "DistanceMatrix");
290        var conversion = echelon.Conversion = (Conversion) data.Conversions[productName].First().Clone(); // TODO: make better choice?
291        echelon.MaxConversionCapacities =
292          data.LocationNames.Select(loc => ConversionCostEvaluator.GetMaxCapacity(conversion, loc)).ToArray();
293        conversion.MaybeInferMainProduct(prices);
294        conversion.Cost += conversion.Products.TotalCost(prices, conversion.MainProduct);
295        Echelons.Add(echelon);
296      }
297      var finalEchelon = new Echelon();
298      var finalProductName = finalEchelon.FeedstockName = data.FinalProducts.CheckedItems.First().Value.Value;
299      Echelons.Add(finalEchelon);
300      FinalProudctPrice = prices[finalProductName];
301    }
302
303    private static V GetValue<K,V>(IDictionary<K,V> d, K key, V defaultValue) {
304      V value;
305      if (d.TryGetValue(key, out value))
306        return value;
307      return defaultValue;
308    }
309
310    private static double GetValue(ValueParameterCollection parameters, string name, double defaultValue) {
311      IValueParameter param = null;
312      if (!parameters.TryGetValue(name, out param)) return defaultValue;
313      var doubleValueParam = param as IValueLookupParameter<DoubleValue>;
314      if (doubleValueParam == null) return defaultValue;
315      return doubleValueParam.Value.Value;
316    }
317
318    [StorableClass(StorableClassType.AllFieldsAndAllProperties)]
319    public sealed class Echelon : Item {
320
321      public Logistic Logistic;
322      public DistanceMatrix DistanceMatrix;
323      public double[] MaxConversionCapacities;
324      public Conversion Conversion;
325      public string FeedstockName;
326
327      [StorableConstructor]
328      private Echelon(bool isDeserializing) : base(isDeserializing) { }
329
330      public Echelon() { }
331
332      private Echelon(Echelon orig, Cloner cloner) {
333        Logistic = cloner.Clone(orig.Logistic);
334        DistanceMatrix = cloner.Clone(orig.DistanceMatrix);
335        MaxConversionCapacities = Util.Clone(orig.MaxConversionCapacities);
336        Conversion = cloner.Clone(orig.Conversion);
337        FeedstockName = Util.Clone(orig.FeedstockName);
338      }
339
340      public override IDeepCloneable Clone(Cloner cloner) {
341        return new Echelon(this, cloner);
342      }
343    }
344
345    public double FinalProudctPrice;
346    public int N;
347    public double[] Potentials;
348    public double[] FeedstockBasePrices;
349    public double[] FeedstockMaxPrices;
350    public double[] invest, maint, fuel, labor, other;
351    public List<Echelon> Echelons; // nr of echelons + 1 for final product
352
353    public double GetLogisticActionCost(LogisticAction a, double amount, int i, int j) {
354      return
355        amount * (
356          a.Investment  *(invest[i] + invest[j]) +
357          a.Maintenance *( maint[i] +  maint[j]) +
358          a.Fuel        *(  fuel[i] +   fuel[j]) +
359          a.Labor       *( labor[i] +  labor[j]) +
360          a.Other       *( other[i] +  other[j]))/2;
361    }
362
363    public double GetLogisticActionCost(LogisticAction a, double amount, int i) {
364      return
365        amount * (
366          a.Investment  * invest[i] +
367          a.Maintenance *  maint[i] +
368          a.Fuel        *   fuel[i] +
369          a.Labor       *  labor[i] +
370          a.Other       *  other[i]);
371    }
372
373    public override IDeepCloneable Clone(Cloner cloner) {
374      return new SimpleProblemData(this, cloner);
375    }
376
377    public static  T GetFromProblemData<T>(BioBoostProblemData data, string label) where T : class, IItem {
378      IParameter param;
379      data.Parameters.TryGetValue(label, out param);
380      var valueParam = param as IValueParameter;
381      if (valueParam != null) return valueParam.Value as T;
382      if (param != null) return param.ActualValue as T;
383      return null;
384    }
385
386  }
387}
Note: See TracBrowser for help on using the repository browser.