Free cookie consent management tool by TermsFeed Policy Generator

source: addons/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost/3.3/Evaluators/MonolithicEvaluator.cs @ 17777

Last change on this file since 17777 was 16575, checked in by gkronber, 5 years ago

#2520: changed HeuristicLab.BioBoost addon to compile with new HL.Persistence

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