Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 13071 was 13071, checked in by gkronber, 8 years ago

#2499: added license headers and removed unused usings

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