#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 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.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using HeuristicLab.BioBoost.Evaluators;
using HeuristicLab.BioBoost.Operators.Mutation;
using HeuristicLab.BioBoost.ProblemDescription;
using HeuristicLab.BioBoost.Utils;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using NetTopologySuite.GeometriesGraph;
using SharpMap;
using SharpMap.Data.Providers;
using SharpMap.Layers;
using SharpMap.Utilities.Wfs;
// using YamlDotNet.RepresentationModel.Serialization.NodeTypeResolvers;
namespace HeuristicLab.BioBoost.Representation {
[StorableClass]
public sealed class BioBoostCompoundSolution : ParameterizedNamedItem, IStorableContent {
public class TransportInfo {
public string Product;
public int SrcRegionIdx;
public int DestRegionIdx;
public string SrcRegion;
public string DestRegion;
public string Mode;
public double Distance;
public double Amount;
}
[Storable]
public Dictionary IntValues;
[Storable]
public Dictionary DoubleValues;
[Storable]
public Dictionary StringValues;
[Storable]
public Dictionary>> TransportTargets;
[Storable]
private BioBoostProblemData problemDataRef;
public BioBoostProblemData ProblemDataReference {
get { return problemDataRef; }
set {
if (problemDataRef != value) {
problemDataRef = value;
EvaluateAndUpdateAllResults(); // also raises OnSolutionChanged (re-evaluation is only done when the problem is set by an external entity)
}
}
}
#region Parameters
public ValueParameter LocationNamesParameter {
get { return (ValueParameter)Parameters["LocationNames"]; }
}
public FixedValueParameter QualityParameter {
get { return (FixedValueParameter)Parameters["Quality"]; }
}
public ValueParameter QualitiesParameter {
get { return (ValueParameter)Parameters["Qualities"]; }
}
#endregion
#region Parameter Values
public StringArray LocationNames {
get { return LocationNamesParameter.Value; }
private set { LocationNamesParameter.Value = value; }
}
public double Quality {
get { return QualityParameter.Value.Value; }
private set { QualityParameter.Value.Value = value; }
}
public DoubleArray Qualities {
get { return QualitiesParameter.Value; }
private set { QualitiesParameter.Value = value; }
}
#endregion
private readonly Closure geometry = new Closure(Closure.Encapsulation.Referenced);
[Storable]
public GeometryFeatureProvider Geometry { get { return geometry.Value; } private set { geometry.Value = value; } }
public event EventHandler SolutionChanged;
private void OnSolutionChanged() {
var handler = SolutionChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
private static bool IsSlimKey(string key) {
if (LayerDescriptor.Utilizations.IsSuffixOf(key)) return true;
if (LayerDescriptor.ConverterCapacities.IsSuffixOf(key)) return true;
if (LayerDescriptor.RelativeCostAtSource.IsSuffixOf(key)) return true;
return false;
}
#region Construction & Cloning
[StorableConstructor]
private BioBoostCompoundSolution(bool isDeserializing) : base(isDeserializing) { }
private BioBoostCompoundSolution(BioBoostCompoundSolution orig, Cloner cloner, bool full = true) : base(orig, cloner) {
problemDataRef = orig.problemDataRef; // doesn't re-evaluate the solution
Geometry = orig.Geometry;
Quality = orig.Quality;
Qualities = (DoubleArray) orig.Qualities.Clone();
IntValues = orig.IntValues.ToDictionary(kvp => kvp.Key, kvp => (IntArray) kvp.Value.Clone());
if (full) {
DoubleValues = orig.DoubleValues.ToDictionary(kvp => kvp.Key, kvp => (DoubleArray) kvp.Value.Clone());
StringValues = orig.StringValues.ToDictionary(kvp => kvp.Key, kvp => (StringArray) kvp.Value.Clone());
if (orig.TransportTargets != null)
TransportTargets = orig.TransportTargets.ToDictionary(kvp => kvp.Key, kvp => (ItemArray>)kvp.Value.Clone());
} else {
DoubleValues = orig.DoubleValues.Where(kvp => IsSlimKey(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => (DoubleArray) kvp.Value.Clone());
StringValues = new Dictionary();
if (orig.TransportTargets != null)
TransportTargets = new Dictionary>>();
}
}
private BioBoostCompoundSolution() {
Parameters.Add(new ValueParameter("LocationNames", "The names of the different locations."));
Parameters.Add(new FixedValueParameter("Quality", "The quality of the solution as produced by the evaluator.", new DoubleValue()));
Parameters.Add(new ValueParameter("Qualities", "The quality criteria of the solution as produced by the evaluator.", new DoubleArray()));
IntValues = new Dictionary();
DoubleValues = new Dictionary();
StringValues = new Dictionary();
TransportTargets = new Dictionary>>();
}
private bool TryAddValue(IScope scope, string valueName) {
IVariable var;
if (!scope.Variables.TryGetValue(valueName, out var))
return false;
var doubleArray = var.Value as DoubleArray;
if (doubleArray != null && !DoubleValues.ContainsKey(valueName)) {
DoubleValues.Add(valueName, (DoubleArray) doubleArray.Clone());
return true;
}
var intArray = var.Value as IntArray;
if (intArray != null && !IntValues.ContainsKey(valueName)) {
IntValues.Add(valueName, (IntArray) intArray.Clone());
return true;
}
var stringArray = var.Value as StringArray;
if (stringArray != null && !StringValues.ContainsKey(valueName)) {
StringValues.Add(valueName, (StringArray) stringArray.Clone());
return true;
}
var itemArray = var.Value as ItemArray>;
if (itemArray != null && !TransportTargets.ContainsKey(valueName)) {
TransportTargets.Add(valueName, (ItemArray>) itemArray.Clone());
}
return false;
}
private bool TryAddValue(BioBoostProblemData problemData, string valueName) {
IParameter param;
if (problemData.FeedstockPotentials.TryGetValue(valueName, out param)) {
var valueParam = param as IValueParameter;
if (valueParam != null) {
var doubleArray = valueParam.Value as DoubleArray;
if (doubleArray != null && !DoubleValues.ContainsKey(valueName)) {
DoubleValues.Add(valueName, (DoubleArray) doubleArray.Clone());
return true;
}
var intArray = valueParam.Value as IntArray;
if (intArray != null && !IntValues.ContainsKey(valueName)) {
IntValues.Add(valueName, (IntArray) intArray.Clone());
return true;
}
var stringArray = valueParam.Value as StringArray;
if (stringArray != null && !StringValues.ContainsKey(valueName)) {
StringValues.Add(valueName, (StringArray) stringArray.Clone());
return true;
}
var itemArray = valueParam.Value as ItemArray>;
if (itemArray != null && !TransportTargets.ContainsKey(valueName)) {
TransportTargets.Add(valueName, (ItemArray>) itemArray.Clone());
return true;
}
}
}
return false;
}
public BioBoostCompoundSolution(IScope scope, BioBoostProblemData problemData) : this() {
this.problemDataRef = problemData; // not cloned on purpose
Geometry = problemData.Geometry; // TODO: maybe clone geometry once?
LocationNames = (StringArray)problemData.LocationNames.Clone();
IVariable qualityVariable;
// TotalCost is the quality produced by the aggregate evaluator
if (scope.Variables.TryGetValue("TotalCost", out qualityVariable)) {
Quality = ((DoubleValue) qualityVariable.Value).Value; // TODO: should be read only
} else if (scope.Variables.TryGetValue("Quality", out qualityVariable)) {
Quality = ((DoubleValue) qualityVariable.Value).Value; // TODO: should be read only
}
if (scope.Variables.TryGetValue("QualityCriteria", out qualityVariable)) {
Qualities = (DoubleArray) qualityVariable.Value.Clone(); // TODO: should be read only
}
var isUpgradedSolution = scope.Variables.Any(v => v.Name.Contains(LayerDescriptor.AmountsAtSource.FullName));
foreach (var p in problemData.Products) {
if (!isUpgradedSolution) {
if (TryAddValue(scope, LayerDescriptor.TransportTargets.NameWithPrefix(p.Label)))
TryAddValue(scope, LayerDescriptor.Utilizations.NameWithPrefix(p.Label));
} else {
TryAddValue(problemData, LayerDescriptor.PotentialsFromProblemData.NameWithPrefix(p.Label));
TryAddValue(scope, p.Label); // emissions
foreach (var layer in LayerDescriptor.Layers) { TryAddValue(scope, layer.NameWithPrefix(p.Label)); }
}
}
if (isUpgradedSolution)
foreach (var layer in LayerDescriptor.Layers) { TryAddValue(scope, layer.Name); }
}
public override IDeepCloneable Clone(Cloner cloner) {
return new BioBoostCompoundSolution(this, cloner);
}
public BioBoostCompoundSolution SlimClone() {
return new BioBoostCompoundSolution(this, new Cloner(), false);
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (StringValues == null)
StringValues = new Dictionary();
// backwards compatibility
if (!Parameters.ContainsKey("Quality"))
Parameters.Add(new FixedValueParameter("Quality", new DoubleValue(double.NaN))); // TODO: use different quality value to indicate that the value is not known?
if(!Parameters.ContainsKey("Qualities"))
Parameters.Add(new ValueParameter("Qualities", "The quality criteria of the solution as produced by the evaluator.", new DoubleArray()));
}
#endregion
public void UpdateTo(BioBoostCompoundSolution bestSolution) {
if (LocationNames.SequenceEqual(bestSolution.LocationNames)) { // TODO: replace with comparison of problem reference
Quality = bestSolution.Quality;
Qualities = (DoubleArray) bestSolution.Qualities.Clone();
IntValues = bestSolution.IntValues.ToDictionary(kvp => kvp.Key, kvp => (IntArray)kvp.Value.Clone());
DoubleValues = bestSolution.DoubleValues.ToDictionary(kvp => kvp.Key, kvp => (DoubleArray)kvp.Value.Clone());
StringValues = bestSolution.StringValues.ToDictionary(kvp => kvp.Key, kvp => (StringArray)kvp.Value.Clone());
TransportTargets = bestSolution.TransportTargets.ToDictionary(kvp => kvp.Key, kvp => (ItemArray>)kvp.Value.Clone());
OnSolutionChanged();
} else {
throw new ArgumentException("Invalid solution update, proposed solution has different problem instance.");
}
}
public static DoubleArray FindAmountsTransportedFromSource(IEnumerable> dict, string productName) {
if (LayerDescriptor.TransportTargets.IsSuffixOf(productName))
productName = LayerDescriptor.TransportTargets.RemoveSuffixFrom(productName);
foreach (var kvp in dict) {
if (LayerDescriptor.AmountsTransportedFromSource.IsSuffixOf(kvp.Key) &&
LayerDescriptor.AmountsTransportedFromSource.RemoveSuffixFrom(kvp.Key) == productName)
return kvp.Value;
}
return null;
}
#region convenience methods for retrieving values
public double TotalFeedstockCosts {
get {
// first get the list of feedstock (all products which are utilized)
var feedStockLabels =
DoubleValues.Keys
.Where(k => LayerDescriptor.Utilizations.IsSuffixOf(k))
.Select(k => LayerDescriptor.Utilizations.RemoveSuffixFrom(k));
return feedStockLabels
.Select(feedstock => DoubleValues[LayerDescriptor.TotalCostsAtSource.NameWithPrefix(feedstock)].Sum()) // sum over regions
.Sum(); // sum over feedstock
}
}
public double TotalTransportCosts {
get {
// here we add transport and handling costs
// keys for all transport costs end with BioBoostProblem.TransportCostsName
// keys for all handling costs end with BioBoostProblem.HandlingCostsName
return DoubleValues
.Where(kvp => LayerDescriptor.TransportCosts.IsSuffixOf(kvp.Key) || LayerDescriptor.HandlingCosts.IsSuffixOf(kvp.Key))
.Select(kvp => kvp.Value.Sum()) // sum over all regions
.Sum(); // sum over all products
}
}
public double TotalCosts {
get {
// we add all costs (including all revenues from selling products)
// assumes that revenues are also labeled as costs!
return DoubleValues
.Where(kvp => kvp.Key.EndsWith("Cost") || kvp.Key.EndsWith("Costs"))
.Sum(kvp => kvp.Value.Where(v => v > 0).Sum()); // sum over products and regions
}
}
public double TotalTransportFuelAmount {
get {
// assumes that the values are stored as "TransportFuel" + BioBoostProblem.AmountsAtSourceName
return DoubleValues
.Where(kvp => kvp.Key == LayerDescriptor.AmountsAtSource.NameWithPrefix("TransportFuel"))
.Sum(kvp => kvp.Value.Sum());
}
}
public IEnumerable Transports {
get {
foreach (var pair in TransportVectors) {
var prod = pair.Item1;
var modes = StringValues[LayerDescriptor.TransportModes.NameWithPrefix(prod)];
var distances = DoubleValues[LayerDescriptor.TransportDistance.NameWithPrefix(prod)];
var amounts = DoubleValues[LayerDescriptor.AmountsTransportedFromSource.NameWithPrefix(prod)];
for (int srcIdx = 0; srcIdx < pair.Item2.Length; srcIdx++) {
var destIdx = pair.Item2[srcIdx];
if (destIdx == -1) continue;
var info = new TransportInfo {
Product = prod,
SrcRegionIdx = srcIdx,
DestRegionIdx = destIdx,
SrcRegion = LocationNames[srcIdx],
DestRegion = LocationNames[destIdx],
Mode = modes[srcIdx],
Distance = distances[srcIdx],
Amount = amounts[srcIdx]
};
yield return info;
}
}
}
}
public IEnumerable> TransportVectors {
get {
// here we yield one tuple for each product containing the product name and transport target map
// we know transport targets are int-arrays and the key ends with 'BioBoostProblem.TransportTargetsName'
foreach (var p in IntValues.Where(p => LayerDescriptor.TransportTargets.IsSuffixOf(p.Key))) {
var productLabel = LayerDescriptor.TransportTargets.RemoveSuffixFrom(p.Key);
yield return Tuple.Create(productLabel, p.Value.ToArray());
}
}
}
public IEnumerable> UtilizationVectors {
get {
// here we yield one tuple for each feedstock containing the feedstock name and the utilization factor
// we know feedstock utilizations are double-arrays and the key ends with 'BioBoostProblem.UtilizationsName'
foreach (var p in DoubleValues.Where(p => LayerDescriptor.Utilizations.IsSuffixOf(p.Key))) {
var productLabel = LayerDescriptor.Utilizations.RemoveSuffixFrom(p.Key);
yield return Tuple.Create(productLabel, p.Value.ToArray());
}
}
}
public double GetUtilization(string feedstock, string currentRegion) {
return DoubleValues[LayerDescriptor.Utilizations.NameWithPrefix(feedstock)][IndexOfLoc(currentRegion)];
}
public void SetUtilization(string feedstock, string currentRegion, double newUtil) {
SetUtilization(feedstock, IndexOfLoc(currentRegion), newUtil);
}
public void SetUtilization(string feedstock, int regionIdx, double newUtil) {
DoubleValues[LayerDescriptor.Utilizations.NameWithPrefix(feedstock)][regionIdx] = newUtil;
EvaluateAndUpdateAllResults();
}
public void SetTransport(string product, int srcIdx, int destIdx) {
IntValues[LayerDescriptor.TransportTargets.NameWithPrefix(product)][srcIdx] = destIdx;
EvaluateAndUpdateAllResults();
}
public void SetTransport(string product, string srcNuts, string destNuts) {
SetTransport(product, IndexOfLoc(srcNuts), IndexOfLoc(destNuts));
}
public void EvaluateAndUpdateAllResults() {
if (problemDataRef == null) {
// for old persisted problems
throw new NotSupportedException("The problem data has not been set for this solution. Editing and re-evaluation is not possible");
}
var evalScope = AggregateEvaluator.Evaluate(
problemDataRef,
DoubleValues.Where(kvp => LayerDescriptor.Utilizations.IsSuffixOf(kvp.Key)),
IntValues.Where(kvp => LayerDescriptor.TransportTargets.IsSuffixOf(kvp.Key))
);
var newSolution = new BioBoostCompoundSolution(evalScope, problemDataRef);
this.UpdateTo(newSolution);
}
private int IndexOfLoc(string locationName) {
for (int i = 0; i < LocationNames.Length; i++) {
if (LocationNames[i] == locationName) return i;
}
return -1;
}
#endregion
public override void CollectParameterValues(IDictionary values) {
base.CollectParameterValues(values);
// also produce additional values (virtual parameters) for most important results
foreach (var kvp in DoubleValues) {
var vFiltered = kvp.Value.Where(x => x > 0);
if (!vFiltered.Any()) continue;
if (kvp.Key.Contains("Relative") || kvp.Key.Contains("Capacities")) {
values.Add(kvp.Key + " (min)", new DoubleValue(vFiltered.Min()));
values.Add(kvp.Key + " (max)", new DoubleValue(vFiltered.Max()));
values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
} else if (kvp.Key.Contains("Utilization")) {
values.Add(kvp.Key + " (count)", new DoubleValue(vFiltered.Count())); // |{u | u > 0}|
values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
} else if (!kvp.Key.Contains("Potentials")) {
values.Add(kvp.Key + " (sum)", new DoubleValue(kvp.Value.Sum()));
values.Add(kvp.Key + " (count)", new DoubleValue(vFiltered.Count()));
values.Add(kvp.Key + " (min)", new DoubleValue(vFiltered.Min()));
values.Add(kvp.Key + " (max)", new DoubleValue(vFiltered.Max()));
values.Add(kvp.Key + " (avg)", new DoubleValue(vFiltered.Average()));
}
}
var finalProductNames = DoubleValues.Keys.Where(k => LayerDescriptor.AmountsAtSource.IsSuffixOf(k))
.Select(k => LayerDescriptor.AmountsAtSource.RemoveSuffixFrom(k));
finalProductNames = finalProductNames
.Where(k => DoubleValues.ContainsKey(LayerDescriptor.DischargeCosts.NameWithPrefix(k)) &&
DoubleValues[LayerDescriptor.DischargeCosts.NameWithPrefix(k)].Sum() < 0);
foreach (var finalProductName in finalProductNames) {
var costs = DoubleValues
.Where(kvp => !kvp.Key.StartsWith(finalProductName))
.Where(kvp => kvp.Key.EndsWith("Costs") || kvp.Key.EndsWith("Cost"))
.Select(kvp => kvp.Value.Sum())
.Sum();
var amount = DoubleValues[LayerDescriptor.AmountsAtSource.NameWithPrefix(finalProductName)].Sum();
values.Add(finalProductName + " net cost [€/a]", new DoubleValue(costs));
values.Add(finalProductName + " net cost [€/t]", new DoubleValue(costs / amount));
}
}
public double PerformPostProcessing(TextWriter logger = null) {
// DeletePenalizedFeedstockTransportsUtilizations();
// DeletePlantsWithScalingFactorBelow(0.8, feedstockName);
// DeletePlantsWithScalingFactorBelow(0.8, intermediateName);
// DeleteEmptyPlants(intermediateName); // maybe this is covered by previous functions?
string feedstockName = null, intermediateName = null;
IDictionary decentralPlantSources = null, centralPlantSources = null;
foreach (var transportTargets in IntValues.Where(kvp => LayerDescriptor.TransportTargets.IsSuffixOf(kvp.Key))) {
var name = LayerDescriptor.TransportTargets.RemoveSuffixFrom(transportTargets.Key);
var utilizationLayerName = LayerDescriptor.Utilizations.NameWithPrefix(name);
var utilizationsKvp = DoubleValues.FirstOrDefault(kvp => kvp.Key == utilizationLayerName);
if (utilizationsKvp.Key != null) {
feedstockName = name;
decentralPlantSources = transportTargets.Value.SourceIndices();
} else {
intermediateName = name;
centralPlantSources = transportTargets.Value.SourceIndices();
}
}
if (feedstockName == null || intermediateName == null || decentralPlantSources == null ||
centralPlantSources == null)
throw new Exception("Post Processing Failed");
var utilizations =
DoubleValues.First(kvp => kvp.Key == LayerDescriptor.Utilizations.NameWithPrefix(feedstockName)).Value;
var feedstockUsageBefore =
utilizations.Zip(
(DoubleArray) ((IValueParameter) problemDataRef.FeedstockPotentials[feedstockName + "Potentials"]).Value,
(u, p) => u*p).Sum();
// remove penalized transports
var feedstockTransportPenaltyLayer =
DoubleValues.First(
kvp => kvp.Key == LayerDescriptor.ExceedingTransportDistancePenalty.NameWithPrefix(feedstockName)).Value;
var removableSuppliers = feedstockTransportPenaltyLayer
.Select((penalty, idx) => new {penalty, idx})
.Where(pi => pi.penalty > 0)
.Select(pi => new List {pi.idx}).ToList();
// remove undersize central plants
var centralConversion = ProblemDataReference.Conversions[feedstockName].First();
var minCentralScalingFactor = centralConversion.MinCapacity/centralConversion.DesignCapacity;
var centralScalingFactors =
DoubleValues.First(kvp => kvp.Key == LayerDescriptor.ScalingFactors.NameWithPrefix(intermediateName)).Value;
removableSuppliers.AddRange(centralScalingFactors
.Select((factor, idx) => new {factor, idx})
.Where(fi => fi.factor < minCentralScalingFactor)
.Select(fi => GetSuppliers(new[] {centralPlantSources, decentralPlantSources}, fi.idx).ToList())
.Where(l => l.Count > 0));
// remove undersize decentral plants
var decentralConversion = ProblemDataReference.Conversions[intermediateName].First();
var minDeCentralScalingFactor = decentralConversion.MinCapacity/decentralConversion.DesignCapacity;
var decentralScalingFactors =
DoubleValues.First(kvp => kvp.Key == LayerDescriptor.ScalingFactors.NameWithPrefix(feedstockName)).Value;
removableSuppliers.AddRange(decentralScalingFactors
.Select((factor, idx) => new {factor, idx})
.Where(fi => fi.factor < minDeCentralScalingFactor)
.Select(fi => GetSuppliers(new[] {decentralPlantSources}, fi.idx).ToList())
.Where(l => l.Count > 0));
EvaluateAndUpdateAllResults();
if (logger != null) logger.Write("Pruning plants (q={0})...", Quality);
int successes = 0;
int k = 0, lastK = 0;
foreach (var variant in removableSuppliers) {
var oldUtilizations = (DoubleArray)utilizations.Clone();
var oldQuality = Quality;
foreach (int j in variant) utilizations[j] = 0;
DoubleValues.Remove(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName));
DoubleValues.Add(LayerDescriptor.Utilizations.NameWithPrefix(feedstockName), utilizations);
EvaluateAndUpdateAllResults();
if (Quality < oldQuality) {
for (int i = 0; i lastK + removableSuppliers.Count/20 && logger != null) {
logger.Write("{0}%...", k*100/removableSuppliers.Count);
lastK = k;
}
}
EvaluateAndUpdateAllResults();
if (logger != null) logger.WriteLine("... pruned {0} of {1} variants (q={2})", successes, removableSuppliers.Count, Quality);
var feedstockUsageAfter =
utilizations.Zip((DoubleArray) ((IValueParameter) problemDataRef.FeedstockPotentials[feedstockName + "Potentials"]).Value,
(u, p) => u*p).Sum();
var utilizationFactor = feedstockUsageAfter/feedstockUsageBefore;
return utilizationFactor;
}
public static IEnumerable GetSuppliers(IEnumerable> suppliers, int i) {
throw new NotImplementedException(); // waiting for source for DictionaryExtensions
// var lists = suppliers.ToList();
// switch (lists.Count) {
// case 0: {
// yield break;
// }
// case 1: {
// foreach (var j in lists[0].ElementAtOrDefault(i, new int[0]))
// yield return j;
// break;
// }
// default: {
// foreach (
// var x in lists[0].ApplyAt(i,
// v => v.Select(j => GetSuppliers(lists.Skip(1), j))
// .Aggregate(Enumerable.Empty(), (l1, l2) => l1.Concat(l2)),
// () => new int[0]))
// yield return x;
// break;
// }
// }
}
#region unused
public void CreateJsonSolution() {
var builder = new StringBuilder();
builder.Append("{ \"type\": \"FeatureCollection\", \n\"features\": [");
for (int i = 0; i < LocationNames.Length; i++) {
if (i > 0) builder.Append(",");
builder.Append("\n{ \"type\": \"Feature\", \"properties\": { \"NUTS_ID\": \"");
builder.Append(LocationNames[i]);
builder.Append("\"");
foreach (var kvp in IntValues) {
var target = LocationNames[i];
if (kvp.Value[i] >= 0) {
target = LocationNames[kvp.Value[i]];
}
builder.Append(",\n \"");
builder.Append(kvp.Key);
builder.Append("\": \"");
builder.Append(target);
builder.Append("\"");
}
foreach (var kvp in DoubleValues) {
builder.Append(",\n \"");
builder.Append(kvp.Key);
builder.Append("\": ");
builder.Append(kvp.Value[i].ToString("R", CultureInfo.InvariantCulture));
}
builder.Append("} }");
}
builder.Append("\n]\n}");
File.WriteAllText(@"c:\temp\test.json", builder.ToString());
}
#endregion
public string Filename { get; set; }
}
}