#region License Information /* HeuristicLab * Copyright (C) 2002-2017 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.Linq; using HeuristicLab.Analysis; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Optimization; using HeuristicLab.Optimization.Operators; using HeuristicLab.Parameters; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Problems.FacilityLocation { [Item("Facility Location Problem (FLP)", "Problem that represents the delivery to multiple customers from a set of depots. Solutions to this problem define which depots to open and which customer to serve from which depot.")] [Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 230)] [StorableClass] public sealed class FacilityLocationProblem : SingleObjectiveBasicProblem { public override bool Maximization { get { return false; } } [Storable] private IValueParameter openingCostsParameter; public IValueParameter OpeningCostsParameter { get { return openingCostsParameter; } } [Storable] private IValueParameter depotCapacitiesParameter; public IValueParameter DepotCapacitiesParameter { get { return depotCapacitiesParameter; } } [Storable] private IValueParameter customerDemandsParameter; public IValueParameter CustomerDemandsParameter { get { return customerDemandsParameter; } } [Storable] private IValueParameter deliveryCostsParameter; public IValueParameter DeliveryCostsParameter { get { return deliveryCostsParameter; } } [StorableConstructor] private FacilityLocationProblem(bool deserializing) : base(deserializing) { } private FacilityLocationProblem(FacilityLocationProblem original, Cloner cloner) : base(original, cloner) { openingCostsParameter = cloner.Clone(original.openingCostsParameter); depotCapacitiesParameter = cloner.Clone(original.depotCapacitiesParameter); customerDemandsParameter = cloner.Clone(original.customerDemandsParameter); deliveryCostsParameter = cloner.Clone(original.deliveryCostsParameter); RegisterEventHandlers(); } public FacilityLocationProblem() { Parameters.Add(openingCostsParameter = new ValueParameter("Opening Costs", "For each possible depot location the costs to open it.")); Parameters.Add(depotCapacitiesParameter = new ValueParameter("Depot Capacities", "For each depot d in D the capacity value which is used to satisfy the customer demands.")); Parameters.Add(customerDemandsParameter = new ValueParameter("Customer Demands", "For each customer c in C the demand that has to be delivered.")); Parameters.Add(deliveryCostsParameter = new ValueParameter("Delivery Costs", "A DxC matrix with service costs for each pair (d, c).")); OpeningCostsParameter.Value = new DoubleArray(new double[] { 55929, 53094, 53842, 47865, 47995, 57991, 55903, 51308, 49140, 59082 } ); DepotCapacitiesParameter.Value = new DoubleArray(new double[] { 490, 420, 420, 490, 560, 490, 490, 490, 420, 560 }); CustomerDemandsParameter.Value = new DoubleArray(new double[] { 14, 19, 16, 18, 16, 14, 18, 12, 14, 20, 19, 20, 19, 18, 19, 18, 20, 20, 19, 15, 19, 18, 12, 19, 20, 19, 11, 16, 20, 17, 15, 19, 19, 11, 20, 12, 14, 19, 12, 20, 12, 20, 15, 16, 13, 15, 18, 19, 12, 19, 18, 20, 13, 17, 17, 11, 16, 16, 17, 16, 18, 16, 18, 19, 18, 17, 16, 13, 16, 15, 11, 15, 13, 16, 18, 15, 11, 14, 12, 16, 17, 17, 12, 14, 14, 15, 19, 16, 19, 12, 16, 11, 11, 14, 17, 15, 17, 20, 15, 12 }); DeliveryCostsParameter.Value = new DoubleMatrix(new double[,] { { 29.73, 30.81, 27.07, 25.08, 20.00, 46.23, 4.24, 28.23, 36.22, 27.02, 51.62, 37.66, 12.04, 25.81, 17.46, 10.44, 5.00, 46.04, 35.69, 39.66, 57.80, 48.09, 33.54, 42.06, 41.19, 41.19, 47.38, 45.28, 43.19, 22.02, 30.61, 55.23, 43.60, 28.86, 24.74, 34.00, 38.00, 17.12, 27.20, 39.62, 36.06, 5.66, 47.63, 24.84, 19.31, 25.96, 17.09, 29.15, 8.06, 18.03, 40.11, 18.25, 38.33, 22.80, 53.82, 49.52, 60.61, 41.11, 30.08, 28.02, 42.58, 7.07, 28.02, 47.01, 32.25, 28.07, 14.76, 41.04, 28.65, 43.19, 7.28, 40.01, 32.53, 45.62, 39.60, 52.40, 31.24, 41.59, 20.10, 21.19, 42.64, 32.02, 31.06, 30.68, 47.41, 31.38, 25.32, 25.00, 10.30, 51.89, 21.93, 49.24, 43.01, 48.80, 23.77, 48.66, 44.28, 49.20, 37.11, 44.78, } , { 8.49, 30.41, 33.84, 25.00, 21.26, 13.15, 33.62, 9.43, 42.94, 34.79, 20.22, 9.49, 26.93, 13.04, 38.01, 37.48, 32.70, 23.41, 9.06, 6.08, 30.02, 44.55, 10.05, 31.89, 39.05, 45.49, 50.61, 17.26, 13.15, 20.02, 17.00, 25.18, 20.62, 42.72, 14.14, 4.00, 40.50, 22.02, 18.97, 35.47, 28.43, 39.85, 21.38, 15.00, 41.05, 41.98, 20.59, 8.60, 39.05, 21.93, 14.32, 20.62, 44.78, 16.12, 28.16, 18.11, 32.14, 11.40, 43.97, 35.11, 37.48, 44.38, 19.31, 45.28, 41.23, 34.18, 27.17, 12.65, 9.22, 13.15, 30.41, 16.16, 13.04, 28.65, 13.42, 19.65, 16.12, 4.24, 36.22, 27.29, 11.05, 15.13, 39.00, 27.29, 28.64, 6.40, 15.00, 13.45, 44.38, 33.24, 43.19, 39.41, 42.64, 18.03, 40.61, 41.23, 6.71, 14.87, 47.42, 29.68, } , { 47.63, 19.24, 20.40, 26.83, 51.89, 42.05, 44.28, 45.61, 11.18, 20.22, 40.52, 55.87, 39.29, 39.41, 30.07, 37.01, 44.41, 29.41, 54.49, 43.01, 37.01, 5.10, 53.91, 16.55, 8.49, 6.32, 4.00, 35.85, 38.47, 34.00, 31.14, 39.46, 30.46, 19.65, 48.55, 44.55, 9.22, 48.85, 30.81, 12.04, 19.10, 43.05, 33.53, 37.44, 29.43, 22.56, 44.78, 46.23, 40.05, 50.16, 60.83, 48.33, 9.49, 38.59, 33.14, 40.16, 39.01, 38.59, 18.97, 19.24, 10.30, 48.26, 30.00, 3.00, 15.13, 19.42, 35.17, 37.22, 42.64, 38.47, 45.89, 62.29, 34.66, 22.80, 35.51, 42.72, 31.83, 48.01, 27.00, 28.46, 40.31, 57.20, 16.00, 21.63, 24.52, 46.75, 50.77, 47.71, 42.58, 24.00, 27.89, 13.04, 5.00, 39.22, 24.33, 10.05, 50.48, 44.38, 13.04, 20.88, } , { 15.00, 14.42, 17.72, 9.06, 22.02, 18.38, 24.84, 13.04, 26.93, 18.68, 22.80, 23.43, 17.03, 7.28, 23.54, 25.06, 24.21, 17.46, 21.93, 14.00, 28.84, 30.53, 21.26, 18.97, 24.21, 29.83, 35.36, 16.28, 14.76, 7.07, 2.00, 26.17, 14.76, 26.68, 16.76, 12.53, 24.84, 20.10, 3.00, 20.81, 13.89, 29.21, 18.60, 6.00, 26.31, 26.17, 16.28, 13.60, 27.46, 21.02, 28.32, 19.03, 28.84, 8.06, 25.06, 20.81, 31.62, 13.00, 27.89, 18.97, 23.32, 34.71, 3.16, 30.81, 25.08, 18.03, 15.00, 12.53, 10.00, 14.76, 23.32, 29.70, 4.12, 19.03, 10.82, 23.85, 2.24, 18.25, 21.10, 12.17, 14.87, 24.70, 22.85, 11.40, 20.22, 14.14, 18.97, 15.81, 32.76, 25.18, 28.07, 27.20, 27.66, 20.00, 25.02, 28.23, 21.21, 21.54, 31.30, 19.03, } , { 15.23, 15.65, 16.28, 8.06, 17.09, 25.55, 17.03, 13.00, 26.91, 17.03, 30.48, 24.70, 9.22, 7.07, 18.03, 18.03, 16.40, 25.06, 22.85, 19.92, 36.62, 33.84, 21.47, 24.02, 26.93, 30.81, 36.89, 24.04, 22.20, 1.00, 9.43, 33.97, 22.47, 24.19, 14.14, 16.00, 26.08, 14.32, 6.32, 24.04, 18.11, 21.63, 26.40, 5.00, 21.10, 22.85, 10.20, 13.93, 20.12, 15.52, 29.07, 13.60, 29.07, 4.47, 32.76, 28.43, 39.41, 20.25, 25.55, 17.69, 26.93, 27.02, 7.28, 33.62, 24.08, 16.97, 7.62, 20.00, 11.18, 22.20, 15.65, 30.02, 11.40, 25.71, 18.44, 31.40, 10.00, 23.19, 16.49, 8.06, 21.95, 23.43, 21.93, 13.60, 27.20, 15.52, 16.28, 13.45, 25.50, 32.02, 23.35, 32.14, 29.97, 27.66, 21.19, 32.56, 26.17, 28.65, 30.48, 25.32, } , { 26.08, 18.68, 15.00, 13.60, 21.63, 38.01, 9.06, 24.02, 24.74, 15.03, 42.44, 35.36, 6.71, 19.24, 7.81, 5.39, 9.22, 35.38, 33.38, 32.57, 47.38, 36.01, 31.58, 30.08, 29.07, 29.61, 35.90, 35.81, 34.48, 13.00, 20.81, 45.45, 33.24, 18.36, 22.80, 28.28, 26.08, 18.03, 17.20, 27.46, 24.17, 10.00, 37.54, 17.46, 10.82, 15.81, 15.23, 25.02, 7.81, 19.42, 39.05, 18.36, 26.93, 16.12, 43.19, 40.50, 50.21, 32.65, 19.72, 16.03, 30.41, 15.81, 17.80, 35.01, 20.88, 16.00, 5.10, 32.25, 23.09, 34.48, 11.18, 39.56, 23.71, 34.13, 30.53, 43.57, 21.63, 35.69, 8.94, 9.43, 34.44, 31.95, 19.42, 18.79, 36.06, 27.00, 24.35, 22.56, 13.04, 40.31, 13.60, 37.11, 31.02, 39.66, 13.60, 36.50, 38.64, 41.15, 26.40, 33.12, } , { 8.06, 35.61, 36.80, 28.32, 9.22, 27.89, 26.02, 9.49, 47.38, 37.54, 34.93, 9.43, 22.14, 15.26, 36.25, 32.98, 25.02, 36.12, 7.81, 20.40, 44.41, 52.84, 5.66, 41.23, 46.32, 51.09, 57.01, 31.06, 27.17, 21.21, 24.17, 39.81, 33.14, 44.41, 6.40, 14.04, 46.23, 12.00, 23.85, 43.05, 36.24, 33.06, 35.01, 17.20, 39.40, 42.72, 13.60, 9.22, 33.38, 11.05, 11.05, 11.05, 49.52, 16.28, 42.05, 32.76, 46.69, 25.08, 45.80, 38.21, 45.65, 36.12, 24.70, 53.00, 44.60, 37.48, 24.60, 26.02, 12.65, 27.17, 22.00, 11.05, 21.84, 40.02, 26.02, 34.48, 23.77, 18.79, 35.85, 28.43, 25.32, 3.16, 42.45, 33.02, 40.61, 10.00, 4.47, 7.07, 38.12, 45.54, 42.05, 49.40, 49.65, 32.56, 40.82, 50.57, 20.25, 29.73, 50.99, 40.52, } , { 21.54, 13.00, 18.25, 12.21, 30.00, 16.03, 31.91, 19.92, 24.74, 19.24, 18.03, 28.60, 24.19, 15.03, 27.29, 30.48, 31.38, 10.00, 27.46, 15.26, 21.93, 24.76, 27.29, 12.21, 19.42, 26.40, 31.06, 11.40, 12.04, 14.32, 6.08, 20.25, 7.81, 27.07, 24.33, 17.20, 21.54, 28.16, 8.94, 15.81, 8.94, 35.38, 12.21, 14.04, 29.55, 27.46, 24.33, 20.25, 33.24, 29.07, 33.60, 27.07, 26.25, 16.12, 17.80, 16.49, 24.76, 11.40, 28.02, 19.10, 17.69, 41.11, 8.06, 25.50, 24.08, 18.11, 21.40, 10.20, 16.76, 12.04, 30.87, 35.23, 7.62, 11.00, 8.25, 19.65, 6.00, 20.25, 24.17, 16.40, 13.34, 31.38, 22.02, 10.05, 12.17, 20.12, 26.48, 23.35, 38.29, 17.12, 30.68, 20.12, 23.02, 15.52, 26.93, 21.63, 22.83, 19.00, 29.61, 11.18, } , { 10.30, 27.73, 27.80, 20.12, 5.10, 29.41, 16.12, 9.22, 38.60, 28.43, 35.85, 18.11, 11.70, 10.20, 25.94, 22.56, 15.13, 33.73, 16.12, 22.02, 44.01, 45.88, 14.04, 35.51, 39.00, 42.72, 48.88, 30.46, 27.29, 13.00, 19.00, 40.25, 30.81, 34.71, 5.10, 15.56, 38.08, 3.61, 17.26, 36.06, 29.83, 23.02, 33.84, 11.00, 29.07, 32.80, 3.16, 10.00, 23.09, 4.12, 21.10, 2.24, 40.80, 9.06, 40.80, 33.62, 46.62, 25.06, 36.12, 29.21, 38.90, 26.68, 18.25, 45.69, 35.47, 28.60, 14.42, 25.50, 10.82, 27.29, 12.37, 21.38, 18.44, 36.01, 24.70, 36.06, 19.03, 22.80, 25.81, 19.21, 26.08, 13.60, 33.42, 25.63, 37.12, 12.21, 6.08, 5.39, 28.00, 42.11, 31.76, 43.74, 42.05, 33.12, 30.81, 44.38, 25.24, 32.02, 41.77, 36.01, } , { 29.73, 32.02, 37.64, 31.76, 42.05, 9.22, 49.50, 29.55, 42.05, 38.60, 2.24, 31.02, 41.73, 28.60, 46.96, 49.65, 48.80, 12.00, 31.14, 16.76, 8.54, 36.40, 32.39, 25.08, 34.13, 42.11, 44.91, 8.60, 10.82, 31.89, 23.60, 3.16, 12.37, 46.10, 34.93, 23.41, 38.00, 41.68, 27.20, 31.02, 26.31, 54.04, 8.06, 29.07, 49.24, 46.84, 38.83, 29.15, 52.20, 42.06, 35.06, 40.31, 43.00, 31.24, 9.22, 4.47, 10.05, 13.04, 46.87, 38.33, 31.26, 59.55, 26.63, 38.08, 42.43, 37.36, 39.82, 12.81, 27.29, 10.82, 47.51, 37.05, 21.21, 18.25, 14.14, 3.16, 22.80, 19.03, 43.86, 35.85, 12.08, 37.48, 40.61, 29.41, 16.49, 27.51, 36.40, 34.01, 57.43, 18.36, 50.33, 28.79, 37.34, 5.00, 46.49, 31.62, 19.10, 8.06, 47.04, 20.22, } }); Encoding.Bounds[0, 0] = 0; Encoding.Bounds[0, 1] = DepotCapacitiesParameter.Value.Length; Encoding.Length = CustomerDemandsParameter.Value.Length; InitializeOperators(); RegisterEventHandlers(); } public override IDeepCloneable Clone(Cloner cloner) { return new FacilityLocationProblem(this, cloner); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterEventHandlers(); } private void InitializeOperators() { Operators.Add(new IntegerVectorHammingSimilarityOperator()); Operators.Add(new QualitySimilarityCalculator()); Operators.Add(new PopulationSimilarityAnalyzer(Operators.OfType())); } private void RegisterEventHandlers() { CustomerDemandsParameter.ValueChanged += CustomerDemandsParameterOnValueChanged; CustomerDemandsParameter.Value.ToStringChanged += CustomerDemandsParameterValueOnToStringChanged; DepotCapacitiesParameter.ValueChanged += DepotCapacitiesParameterOnValueChanged; DepotCapacitiesParameter.Value.ToStringChanged += DepotCapacitiesParameterValueOnToStringChanged; } private void CustomerDemandsParameterOnValueChanged(object sender, EventArgs eventArgs) { CustomerDemandsParameter.Value.ToStringChanged += CustomerDemandsParameterValueOnToStringChanged; Encoding.Length = CustomerDemandsParameter.Value.Length; } private void CustomerDemandsParameterValueOnToStringChanged(object sender, EventArgs eventArgs) { Encoding.Length = CustomerDemandsParameter.Value.Length; } private void DepotCapacitiesParameterOnValueChanged(object sender, EventArgs eventArgs) { DepotCapacitiesParameter.Value.ToStringChanged += DepotCapacitiesParameterValueOnToStringChanged; Encoding.Bounds[0, 0] = 0; Encoding.Bounds[0, 1] = DepotCapacitiesParameter.Value.Length; } private void DepotCapacitiesParameterValueOnToStringChanged(object sender, EventArgs eventArgs) { Encoding.Bounds[0, 0] = 0; Encoding.Bounds[0, 1] = DepotCapacitiesParameter.Value.Length; } public override double Evaluate(Individual individual, IRandom random) { var vec = individual.IntegerVector(Encoding.Name); return Evaluate(vec); } public double Evaluate(IntegerVector depotAssignment) { var openingCosts = OpeningCostsParameter.Value; var capacities = DepotCapacitiesParameter.Value; var demands = CustomerDemandsParameter.Value; var deliveryCosts = DeliveryCostsParameter.Value; double totalOpeningCosts, totalDeliveryCosts; double[] depotUsage; try { return Evaluate(depotAssignment, openingCosts, capacities, demands, deliveryCosts, out totalOpeningCosts, out totalDeliveryCosts, out depotUsage); } catch (IndexOutOfRangeException) { throw new ArgumentException("The solution contains more customers than there are problem data for."); } } public FacilityLocationSolution GetSolution(IntegerVector depotAssignment) { var openingCosts = OpeningCostsParameter.Value; var capacities = DepotCapacitiesParameter.Value; var demands = CustomerDemandsParameter.Value; var deliveryCosts = DeliveryCostsParameter.Value; double totalOpeningCosts, totalDeliveryCosts, fitness; double[] depotUsage; try { fitness = Evaluate(depotAssignment, openingCosts, capacities, demands, deliveryCosts, out totalOpeningCosts, out totalDeliveryCosts, out depotUsage); } catch (IndexOutOfRangeException) { throw new ArgumentException("The solution contains more customers than there are problem data for."); } var overbooking = depotUsage.Zip(capacities, (a, b) => a > b ? a - b : 0).Sum(); return new FacilityLocationSolution() { OpeningCosts = (DoubleArray)openingCosts.Clone(), DepotCapacities = (DoubleArray)capacities.Clone(), CustomerDemands = (DoubleArray)demands.Clone(), DeliveryCosts = (DoubleMatrix)deliveryCosts.Clone(), CustomerToDepotAssignmentParameter = { Value = (IntegerVector)depotAssignment.Clone() }, FitnessValueParameter = { Value = new DoubleValue(fitness) }, TotalOpeningCostsParameter = { Value = new DoubleValue(totalOpeningCosts) }, TotalDeliveryCostsParameter = { Value = new DoubleValue(totalDeliveryCosts) }, TotalOverbookedCapacityParameter = { Value = new DoubleValue(overbooking) } }; } public static double Evaluate(IEnumerable customer2Depot, DoubleArray openingCosts, DoubleArray capacities, DoubleArray demands, DoubleMatrix deliveryCosts, out double totalOpeningCosts, out double totalDeliveryCosts, out double[] capacityDemandsPerDepot) { var D = capacities.Length; capacityDemandsPerDepot = new double[D]; totalOpeningCosts = 0.0; totalDeliveryCosts = 0.0; var customer = 0; foreach (var depot in customer2Depot) { if (capacityDemandsPerDepot[depot] == 0) totalOpeningCosts += openingCosts[depot]; capacityDemandsPerDepot[depot] += demands[customer]; totalDeliveryCosts += deliveryCosts[depot, customer]; customer++; } var overbooking = capacityDemandsPerDepot.Zip(capacities, (a, b) => a > b ? a - b : 0).Sum(); if (overbooking > 0) // infeasible return openingCosts.Sum() + deliveryCosts.Sum() + overbooking; return totalOpeningCosts + totalDeliveryCosts; } public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) { var orderedIndividuals = individuals.Zip(qualities, (i, q) => new { Individual = i, Quality = q }).OrderBy(z => z.Quality); var best = Maximization ? orderedIndividuals.Last() : orderedIndividuals.First(); var bestSol = best.Individual.IntegerVector(Encoding.Name); FacilityLocationSolution bestSoFar = null; IResult bestSoFarResult; results.TryGetValue("Best Solution", out bestSoFarResult); if (bestSoFarResult != null) bestSoFar = bestSoFarResult.Value as FacilityLocationSolution; if (bestSoFar == null || bestSoFar.FitnessValueParameter.Value.Value > best.Quality) { var openingCosts = OpeningCostsParameter.Value; var capacities = DepotCapacitiesParameter.Value; var demands = CustomerDemandsParameter.Value; var deliveryCosts = DeliveryCostsParameter.Value; double totalOpeningCosts, totalDeliveryCosts; double[] depotUsage; try { Evaluate(bestSol, openingCosts, capacities, demands, deliveryCosts, out totalOpeningCosts, out totalDeliveryCosts, out depotUsage); } catch (IndexOutOfRangeException) { throw new ArgumentException("The solution contains more customers than there are problem data for."); } var overbooking = depotUsage.Zip(capacities, (a, b) => a > b ? a - b : 0).Sum(); if (bestSoFar == null) { bestSoFar = new FacilityLocationSolution() { OpeningCosts = (DoubleArray)openingCosts.Clone(), DepotCapacities = (DoubleArray)capacities.Clone(), CustomerDemands = (DoubleArray)demands.Clone(), DeliveryCosts = (DoubleMatrix)deliveryCosts.Clone(), CustomerToDepotAssignmentParameter = {Value = (IntegerVector)bestSol.Clone()}, FitnessValueParameter = {Value = new DoubleValue(best.Quality)}, TotalOpeningCostsParameter = {Value = new DoubleValue(totalOpeningCosts)}, TotalDeliveryCostsParameter = {Value = new DoubleValue(totalDeliveryCosts)}, TotalOverbookedCapacityParameter = {Value = new DoubleValue(overbooking)} }; } else { bestSoFar.CustomerToDepotAssignmentParameter.Value = (IntegerVector)bestSol.Clone(); bestSoFar.FitnessValueParameter.Value.Value = best.Quality; bestSoFar.TotalOpeningCostsParameter.Value.Value = totalOpeningCosts; bestSoFar.TotalDeliveryCostsParameter.Value.Value = totalDeliveryCosts; bestSoFar.TotalOverbookedCapacityParameter.Value.Value = overbooking; } } if (bestSoFarResult == null) { bestSoFarResult = new Result("Best Solution", bestSoFar); results.Add(bestSoFarResult); } } } }