#region License Information /* HeuristicLab * Copyright (C) 2002-2019 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.IO; 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 HEAL.Attic; using HeuristicLab.Problems.Instances; using HeuristicLab.Problems.Instances.Types; namespace HeuristicLab.Problems.Orienteering { [Item("Orienteering Problem (OP)", "Represents a single-objective Orienteering Problem.")] [Creatable(CreatableAttribute.Categories.CombinatorialProblems, Priority = 115)] [StorableType("0B8DB4A4-F183-4368-86C6-C51289B183D2")] public sealed class OrienteeringProblem : SingleObjectiveHeuristicOptimizationProblem, IStorableContent, IProblemInstanceConsumer, IProblemInstanceConsumer, IProblemInstanceConsumer { public string Filename { get; set; } #region Parameter Properties public OptionalValueParameter CoordinatesParameter { get { return (OptionalValueParameter)Parameters["Coordinates"]; } } public IValueParameter DistanceMatrixParameter { get { return (IValueParameter)Parameters["DistanceMatrix"]; } } public IFixedValueParameter StartingPointParameter { get { return (IFixedValueParameter)Parameters["StartingPoint"]; } } public IFixedValueParameter TerminalPointParameter { get { return (IFixedValueParameter)Parameters["TerminalPoint"]; } } public IFixedValueParameter MaximumDistanceParameter { get { return (IFixedValueParameter)Parameters["MaximumDistance"]; } } public IValueParameter ScoresParameter { get { return (IValueParameter)Parameters["Scores"]; } } public IFixedValueParameter PointVisitingCostsParameter { get { return (IFixedValueParameter)Parameters["PointVisitingCosts"]; } } public OptionalValueParameter BestKnownSolutionParameter { get { return (OptionalValueParameter)Parameters["BestKnownSolution"]; } } #endregion #region Properties public DoubleMatrix Coordinates { get { return CoordinatesParameter.Value; } set { CoordinatesParameter.Value = value; } } public DistanceMatrix DistanceMatrix { get { return DistanceMatrixParameter.Value; } set { DistanceMatrixParameter.Value = value; } } public int StartingPoint { get { return StartingPointParameter.Value.Value; } set { StartingPointParameter.Value.Value = value; } } public int TerminalPoint { get { return TerminalPointParameter.Value.Value; } set { TerminalPointParameter.Value.Value = value; } } public double MaximumDistance { get { return MaximumDistanceParameter.Value.Value; } set { MaximumDistanceParameter.Value.Value = value; } } public DoubleArray Scores { get { return ScoresParameter.Value; } set { ScoresParameter.Value = value; } } public double PointVisitingCosts { get { return PointVisitingCostsParameter.Value.Value; } set { PointVisitingCostsParameter.Value.Value = value; } } public IntegerVector BestKnownSolution { get { return BestKnownSolutionParameter.Value; } set { BestKnownSolutionParameter.Value = value; } } private BestOrienteeringSolutionAnalyzer BestOrienteeringSolutionAnalyser { get { return Operators.OfType().SingleOrDefault(); } } #endregion [StorableConstructor] private OrienteeringProblem(StorableConstructorFlag _) : base(_) { } private OrienteeringProblem(OrienteeringProblem original, Cloner cloner) : base(original, cloner) { RegisterEventHandlers(); } public override IDeepCloneable Clone(Cloner cloner) { return new OrienteeringProblem(this, cloner); } public OrienteeringProblem() : base(new OrienteeringEvaluator(), new GreedyOrienteeringTourCreator()) { Parameters.Add(new OptionalValueParameter("Coordinates", "The x- and y-Coordinates of the points.")); Parameters.Add(new ValueParameter("DistanceMatrix", "The matrix which contains the distances between the points.")); Parameters.Add(new FixedValueParameter("StartingPoint", "Index of the starting point.", new IntValue(0))); Parameters.Add(new FixedValueParameter("TerminalPoint", "Index of the ending point.", new IntValue(0))); Parameters.Add(new FixedValueParameter("MaximumDistance", "The maximum distance constraint for a Orienteering solution.")); Parameters.Add(new ValueParameter("Scores", "The scores of the points.")); Parameters.Add(new FixedValueParameter("PointVisitingCosts", "The costs for visiting a point.")); Parameters.Add(new OptionalValueParameter("BestKnownSolution", "The best known solution of this Orienteering instance.")); Maximization.Value = true; MaximizationParameter.Hidden = true; SolutionCreator.IntegerVectorParameter.ActualName = "OrienteeringSolution"; InitializeInitialOrienteeringInstance(); ParameterizeSolutionCreator(); ParameterizeEvaluator(); InitializeOperators(); RegisterEventHandlers(); } #region Events protected override void OnSolutionCreatorChanged() { base.OnSolutionCreatorChanged(); SolutionCreator.IntegerVectorParameter.ActualNameChanged += SolutionCreator_IntegerVectorParameter_ActualNameChanged; ParameterizeSolutionCreator(); ParameterizeEvaluator(); ParameterizeAnalyzer(); ParameterizeOperators(); } protected override void OnEvaluatorChanged() { base.OnEvaluatorChanged(); ParameterizeEvaluator(); ParameterizeAnalyzer(); } private void SolutionCreator_IntegerVectorParameter_ActualNameChanged(object sender, EventArgs e) { ParameterizeEvaluator(); ParameterizeAnalyzer(); ParameterizeOperators(); } private void CoordinatesParameter_ValueChanged(object sender, EventArgs e) { if (Coordinates != null) { Coordinates.ItemChanged += new EventHandler>(CoordinatesValue_ItemChanged); Coordinates.Reset += new EventHandler(CoordinatesValue_Reset); } ParameterizeSolutionCreator(); UpdateDistanceMatrix(); CheckStartingIndex(); CheckTerminalIndex(); } private void CoordinatesValue_ItemChanged(object sender, EventArgs e) { UpdateDistanceMatrix(); CheckStartingIndex(); CheckTerminalIndex(); } private void CoordinatesValue_Reset(object sender, EventArgs e) { ParameterizeSolutionCreator(); UpdateDistanceMatrix(); CheckStartingIndex(); CheckTerminalIndex(); } private void StartingPointParameterValue_ValueChanged(object sender, EventArgs e) { CheckStartingIndex(); } private void TerminalPointParameterValue_ValueChanged(object sender, EventArgs e) { CheckTerminalIndex(); } private void MaximumDistanceParameterValue_ValueChanged(object sender, EventArgs e) { } private void ScoresParameter_ValueChanged(object sender, EventArgs e) { ParameterizeEvaluator(); ParameterizeAnalyzer(); ParameterizeSolutionCreator(); ScoresParameter.Value.Reset += new EventHandler(ScoresValue_Reset); } private void ScoresValue_Reset(object sender, EventArgs e) { ParameterizeSolutionCreator(); } private void PointVisitingCostsParameterValue_ValueChanged(object sender, EventArgs e) { } private void BestKnownSolutionParameter_ValueChanged(object sender, EventArgs e) { if (BestKnownSolution == null) BestKnownQuality = null; } #endregion #region Helpers [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterEventHandlers(); } private void RegisterEventHandlers() { SolutionCreator.IntegerVectorParameter.ActualNameChanged += SolutionCreator_IntegerVectorParameter_ActualNameChanged; CoordinatesParameter.ValueChanged += CoordinatesParameter_ValueChanged; if (CoordinatesParameter.Value != null) { CoordinatesParameter.Value.ItemChanged += CoordinatesValue_ItemChanged; CoordinatesParameter.Value.Reset += CoordinatesValue_Reset; } StartingPointParameter.Value.ValueChanged += StartingPointParameterValue_ValueChanged; TerminalPointParameter.Value.ValueChanged += TerminalPointParameterValue_ValueChanged; MaximumDistanceParameter.Value.ValueChanged += MaximumDistanceParameterValue_ValueChanged; PointVisitingCostsParameter.Value.ValueChanged += PointVisitingCostsParameterValue_ValueChanged; ScoresParameter.ValueChanged += ScoresParameter_ValueChanged; ScoresParameter.Value.Reset += ScoresValue_Reset; BestKnownSolutionParameter.ValueChanged += BestKnownSolutionParameter_ValueChanged; } private void ParameterizeSolutionCreator() { SolutionCreator.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name; SolutionCreator.ScoresParameter.ActualName = ScoresParameter.Name; SolutionCreator.MaximumDistanceParameter.ActualName = MaximumDistanceParameter.Name; SolutionCreator.StartingPointParameter.ActualName = StartingPointParameter.Name; SolutionCreator.TerminalPointParameter.ActualName = TerminalPointParameter.Name; SolutionCreator.PointVisitingCostsParameter.ActualName = PointVisitingCostsParameter.Name; } private void ParameterizeEvaluator() { Evaluator.IntegerVectorParameter.ActualName = SolutionCreator.IntegerVectorParameter.ActualName; Evaluator.ScoresParameter.ActualName = ScoresParameter.Name; Evaluator.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name; Evaluator.MaximumDistanceParameter.ActualName = MaximumDistanceParameter.Name; Evaluator.PointVisitingCostsParameter.ActualName = PointVisitingCostsParameter.Name; } private void ParameterizeAnalyzer() { if (BestOrienteeringSolutionAnalyser != null) { BestOrienteeringSolutionAnalyser.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName; BestOrienteeringSolutionAnalyser.IntegerVector.ActualName = SolutionCreator.IntegerVectorParameter.ActualName; BestOrienteeringSolutionAnalyser.CoordinatesParameter.ActualName = CoordinatesParameter.Name; BestOrienteeringSolutionAnalyser.ScoresParameter.ActualName = ScoresParameter.Name; BestOrienteeringSolutionAnalyser.ResultsParameter.ActualName = "Results"; BestOrienteeringSolutionAnalyser.BestKnownQualityParameter.ActualName = BestKnownQualityParameter.Name; BestOrienteeringSolutionAnalyser.BestKnownSolutionParameter.ActualName = BestKnownSolutionParameter.Name; } } private void InitializeOperators() { Operators.Add(new BestOrienteeringSolutionAnalyzer()); ParameterizeAnalyzer(); Operators.Add(new OrienteeringLocalImprovementOperator()); Operators.Add(new OrienteeringShakingOperator()); Operators.Add(new QualitySimilarityCalculator()); Operators.Add(new PopulationSimilarityAnalyzer(Operators.OfType())); ParameterizeOperators(); } private void ParameterizeOperators() { foreach (var op in Operators.OfType()) { op.IntegerVectorParameter.ActualName = SolutionCreator.IntegerVectorParameter.ActualName; op.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name; op.ScoresParameter.ActualName = ScoresParameter.Name; op.MaximumDistanceParameter.ActualName = MaximumDistanceParameter.Name; op.StartingPointParameter.ActualName = StartingPointParameter.Name; op.TerminalPointParameter.ActualName = TerminalPointParameter.Name; op.PointVisitingCostsParameter.ActualName = PointVisitingCostsParameter.Name; } foreach (var op in Operators.OfType()) { op.IntegerVectorParameter.ActualName = SolutionCreator.IntegerVectorParameter.ActualName; op.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name; op.ScoresParameter.ActualName = ScoresParameter.Name; op.MaximumDistanceParameter.ActualName = MaximumDistanceParameter.Name; op.StartingPointParameter.ActualName = StartingPointParameter.Name; op.TerminalPointParameter.ActualName = TerminalPointParameter.Name; op.PointVisitingCostsParameter.ActualName = PointVisitingCostsParameter.Name; } foreach (var similarityCalculator in Operators.OfType()) { similarityCalculator.SolutionVariableName = SolutionCreator.IntegerVectorParameter.ActualName; similarityCalculator.QualityVariableName = Evaluator.QualityParameter.ActualName; } } #endregion private DistanceMatrix CalculateDistanceMatrix(double[,] coordinates) { var distances = DistanceHelper.GetDistanceMatrix(DistanceMeasure.Euclidean, coordinates, null, coordinates.GetLength(0)); return new DistanceMatrix(distances); } private void UpdateDistanceMatrix() { if (Coordinates == null) { DistanceMatrix = new DistanceMatrix(0, 0); return; } var coordinates = Coordinates; int dimension = coordinates.Rows; var distances = new double[dimension, dimension]; for (int i = 0; i < dimension - 1; i++) { for (int j = i + 1; j < dimension; j++) { double x1 = coordinates[i, 0]; double y1 = coordinates[i, 1]; double x2 = coordinates[j, 0]; double y2 = coordinates[j, 1]; distances[i, j] = Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); distances[j, i] = distances[i, j]; } } DistanceMatrix = new DistanceMatrix(distances); } private void CheckStartingIndex() { if (StartingPoint < 0) StartingPoint = 0; if (StartingPoint >= DistanceMatrix.Rows) StartingPoint = DistanceMatrix.Rows - 1; } private void CheckTerminalIndex() { if (TerminalPoint < 0) TerminalPoint = 0; if (TerminalPoint >= DistanceMatrix.Rows) TerminalPoint = DistanceMatrix.Rows - 1; } private void InitializeInitialOrienteeringInstance() { var coordinates = new double[21, 2] { { 4.60, 7.10 }, { 5.70, 11.40 }, { 4.40, 12.30 }, { 2.80, 14.30 }, { 3.20, 10.30 }, { 3.50, 9.80 }, { 4.40, 8.40 }, { 7.80, 11.00 }, { 8.80, 9.80 }, { 7.70, 8.20 }, { 6.30, 7.90 }, { 5.40, 8.20 }, { 5.80, 6.80 }, { 6.70, 5.80 }, { 13.80, 13.10 }, { 14.10, 14.20 }, { 11.20, 13.60 }, { 9.70, 16.40 }, { 9.50, 18.80 }, { 4.70, 16.80 }, { 5.00, 5.60 } }; Coordinates = new DoubleMatrix(coordinates); DistanceMatrix = CalculateDistanceMatrix(coordinates); StartingPoint = 0; TerminalPoint = 20; MaximumDistance = 30; Scores = new DoubleArray(new double[21] { 0, 20, 20, 30, 15, 15, 10, 20, 20, 20, 15, 10, 10, 25, 40, 40, 30, 30, 50, 30, 0 }); } #region Instance consuming public void Load(OPData data) { if (data.Coordinates == null && data.Distances == null) throw new InvalidDataException("The given instance specifies no coordinates or distance matrix!"); if (data.Coordinates != null && data.Coordinates.GetLength(1) != 2) throw new InvalidDataException("The coordinates of the given instance are not in the right format, there need to be one row for each customer and two columns for the x and y coordinates."); // Clear old solutions BestKnownSolution = null; Name = data.Name; Description = data.Description; Coordinates = data.Coordinates != null ? new DoubleMatrix(data.Coordinates) : null; if (data.Distances != null) DistanceMatrix = new DistanceMatrix(data.Distances); else DistanceMatrix = new DistanceMatrix(data.GetDistanceMatrix()); StartingPoint = data.StartingPoint; TerminalPoint = data.TerminalPoint; PointVisitingCosts = data.PointVisitingCosts; MaximumDistance = data.MaximumDistance; Scores = new DoubleArray(data.Scores); } public void Load(TSPData data) { if (data.Coordinates == null && data.Distances == null) throw new InvalidDataException("The given instance specifies no coordinates or distance matrix!"); if (data.Coordinates != null && data.Coordinates.GetLength(1) != 2) throw new InvalidDataException("The coordinates of the given instance are not in the right format, there need to be one row for each customer and two columns for the x and y coordinates."); // Clear old solutions BestKnownSolution = null; Name = data.Name; Description = data.Description; Coordinates = data.Coordinates != null ? new DoubleMatrix(data.Coordinates) : null; if (data.Distances != null) DistanceMatrix = new DistanceMatrix(data.Distances); else DistanceMatrix = new DistanceMatrix(data.GetDistanceMatrix()); StartingPoint = 0; // First city is interpreted as start point TerminalPoint = data.Dimension - 1; // Last city is interpreted als end point PointVisitingCosts = 0; MaximumDistance = DistanceMatrix.Average() * 5.0; // distance from start to end first to last city is interpreted as maximum distance Scores = new DoubleArray(Enumerable.Repeat(1.0, data.Dimension).ToArray()); // all scores are 1 } public void Load(CVRPData data) { if (data.Coordinates == null && data.Distances == null) throw new InvalidDataException("The given instance specifies no coordinates or distance matrix!"); if (data.Coordinates != null && data.Coordinates.GetLength(1) != 2) throw new InvalidDataException("The coordinates of the given instance are not in the right format, there need to be one row for each customer and two columns for the x and y coordinates."); // Clear old solutions BestKnownSolution = null; Name = data.Name; Description = data.Description; Coordinates = data.Coordinates != null ? new DoubleMatrix(data.Coordinates) : null; DistanceMatrix = data.Distances != null ? new DistanceMatrix(data.Distances) : CalculateDistanceMatrix(data.Coordinates); StartingPoint = 0; // Depot is interpreted as start point TerminalPoint = 0; // Depot is interpreted als end point PointVisitingCosts = 0; MaximumDistance = data.Capacity * 2; // capacity is interpreted as max distance Scores = new DoubleArray(data.Demands); // demands are interpreted as scores } #endregion } }