using System; using System.Collections.Generic; using System.Linq; using System.Text; using HeuristicLab.BioBoost.Data; using HeuristicLab.Core; using HeuristicLab.Data; using SharpMap.Data; namespace HeuristicLab.BioBoost.ProblemDescription { public class SolutionSpaceDiscoverer { private enum ProductCheck { None, NonFeasible, InProgress, Feasible }; // caches private readonly List products; private readonly HashSet transportableProductNames; private readonly List conversions; private readonly Dictionary prices; private readonly Dictionary> choices; // status private readonly Dictionary currentChecks; // results public List FeasibleFeedstocks { get; private set; } public List FeasibleTransports { get; private set; } public Dictionary> FeasibleChoices { get; private set; } public Dictionary FeasibilityAnalysis { get; private set; } public int MaxDepth { get; private set; } public SolutionSpaceDiscoverer(IEnumerable feedstockNames, List products, IEnumerable logistics, IEnumerable conversions) { this.products = products.ToList(); transportableProductNames = new HashSet(); foreach (var l in logistics) { transportableProductNames.Add(l.Product); } this.conversions = conversions.ToList(); this.prices = products.ToDictionary(p => p.Label, p => p.Price); this.choices = new Dictionary>(); this.currentChecks = products.ToDictionary(p => p, p => ProductCheck.None); this.FeasibilityAnalysis = products.ToDictionary(p => p.Label, p => string.Empty); this.FeasibleFeedstocks = new List(); this.FeasibleTransports = new List(); this.MaxDepth = 0; var feedstocks = new HashSet(feedstockNames); var feedstockProducts = products.Where(p => feedstocks.Contains(p.Label)).ToList(); foreach (var input in feedstockProducts) { if (CheckProduct(input)) { FeasibleFeedstocks.Add(input.Label); } } foreach (var pair in currentChecks) { if (pair.Value == ProductCheck.Feasible) { FeasibleTransports.Add(pair.Key.Label); List newChoices = null; if (choices.TryGetValue(pair.Key, out newChoices)) { FeasibleChoices.Add(pair.Key.Label, newChoices.Select(c => c.Label).ToList()); } } } } public SolutionSpaceDiscoverer(IEnumerable feedstockNames, List dataItems) : this(feedstockNames, dataItems.OfType().ToList(), dataItems.OfType(), dataItems.OfType()) {} private bool CheckProduct(Product inputProduct, int currentDepth = 1) { switch (currentChecks[inputProduct]) { case ProductCheck.Feasible: return true; case ProductCheck.NonFeasible: return false; case ProductCheck.InProgress: FeasibilityAnalysis[inputProduct.Label] += "not checked recursively: circular dependency"; return false; case ProductCheck.None: break; } currentChecks[inputProduct] = ProductCheck.InProgress; // transportable? if (!transportableProductNames.Contains(inputProduct.Label)) { currentChecks[inputProduct] = ProductCheck.NonFeasible; FeasibilityAnalysis[inputProduct.Label] += "NOT transportable"; return false; // not transportable } // convertable? var otherOutputProducts = new HashSet(); var profitableOutputProducts = new HashSet(); var profitableConversions = new List(); foreach (var c in conversions.Where(c => c.Feedstock == inputProduct.Label)) { // outputs? foreach (var outputProductParameter in c.Products) { var outputProductName = outputProductParameter.Name; var outputAmount = outputProductParameter.Value as DoubleValue; if (string.IsNullOrEmpty(outputProductName)) continue; // no product var product = products.FirstOrDefault(p2 => p2.Label == outputProductName); if (product == null || outputAmount == null) continue; // not a real product double amount = outputAmount.Value; double price; if (prices.TryGetValue(product.Label, out price) && price*amount > 0) { profitableOutputProducts.Add(product); } else { otherOutputProducts.Add(product); } } // recurse foreach (var p in profitableOutputProducts) CheckProduct(p, currentDepth + 1); var otherProfits = otherOutputProducts.Any(p => CheckProduct(p, currentDepth + 1)); // analyze if (profitableOutputProducts.Count == 0 && !otherProfits) { currentChecks[inputProduct] = ProductCheck.NonFeasible; FeasibilityAnalysis[inputProduct.Label] += "NO profitable direct or indirect output products"; } else { currentChecks[inputProduct] = ProductCheck.Feasible; MaxDepth = Math.Max(MaxDepth, currentDepth); profitableConversions.Add(c); if (profitableOutputProducts.Count > 0) FeasibilityAnalysis[inputProduct.Label] += "profitable direct output products "; if (otherProfits) FeasibilityAnalysis[inputProduct.Label] += "profitable indirect output products"; } } // conclusion if (profitableConversions.Count > 1) choices.Add(inputProduct, profitableConversions); return profitableConversions.Count > 0; } } }