#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.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HEAL.Attic;
using HeuristicLab.Problems.Instances;
using HeuristicLab.Random;
namespace HeuristicLab.Problems.PTSP {
[Item("Estimated Probabilistic Traveling Salesman Problem (PTSP)", "Represents a probabilistic traveling salesman problem where the expected tour length is estimated by averaging over the length of tours on a number of, so called, realizations.")]
[Creatable(CreatableAttribute.Categories.CombinatorialProblems)]
[StorableType("D1F1DE71-54E3-40B6-856F-685CD71D97F9")]
public sealed class EstimatedProbabilisticTravelingSalesmanProblem : ProbabilisticTravelingSalesmanProblem {
#region Parameter Properties
public IValueParameter> RealizationsParameter {
get { return (IValueParameter>)Parameters["Realizations"]; }
}
public IFixedValueParameter RealizationsSizeParameter {
get { return (IFixedValueParameter)Parameters["RealizationsSize"]; }
}
#endregion
#region Properties
public ItemList Realizations {
get { return RealizationsParameter.Value; }
set { RealizationsParameter.Value = value; }
}
public int RealizationsSize {
get { return RealizationsSizeParameter.Value.Value; }
set { RealizationsSizeParameter.Value.Value = value; }
}
#endregion
[StorableConstructor]
private EstimatedProbabilisticTravelingSalesmanProblem(StorableConstructorFlag _) : base(_) { }
private EstimatedProbabilisticTravelingSalesmanProblem(EstimatedProbabilisticTravelingSalesmanProblem original, Cloner cloner)
: base(original, cloner) {
RegisterEventHandlers();
}
public EstimatedProbabilisticTravelingSalesmanProblem() {
Parameters.Add(new FixedValueParameter("RealizationsSize", "Size of the sample for the estimation-based evaluation", new IntValue(100)));
Parameters.Add(new ValueParameter>("Realizations", "The list of samples drawn from all possible stochastic instances.", new ItemList()));
Operators.Add(new BestPTSPSolutionAnalyzer());
Operators.Add(new PTSPEstimatedInversionMoveEvaluator());
Operators.Add(new PTSPEstimatedInsertionMoveEvaluator());
Operators.Add(new PTSPEstimatedInversionLocalImprovement());
Operators.Add(new PTSPEstimatedInsertionLocalImprovement());
Operators.Add(new PTSPEstimatedTwoPointFiveLocalImprovement());
Operators.Add(new ExhaustiveTwoPointFiveMoveGenerator());
Operators.Add(new StochasticTwoPointFiveMultiMoveGenerator());
Operators.Add(new StochasticTwoPointFiveSingleMoveGenerator());
Operators.Add(new TwoPointFiveMoveMaker());
Operators.Add(new PTSPEstimatedTwoPointFiveMoveEvaluator());
Operators.RemoveAll(x => x is SingleObjectiveMoveGenerator);
Operators.RemoveAll(x => x is SingleObjectiveMoveMaker);
Operators.RemoveAll(x => x is SingleObjectiveMoveEvaluator);
Encoding.ConfigureOperators(Operators.OfType());
foreach (var twopointfiveMoveOperator in Operators.OfType()) {
twopointfiveMoveOperator.TwoPointFiveMoveParameter.ActualName = "Permutation.TwoPointFiveMove";
}
UpdateRealizations();
RegisterEventHandlers();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new EstimatedProbabilisticTravelingSalesmanProblem(this, cloner);
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
RegisterEventHandlers();
}
private void RegisterEventHandlers() {
RealizationsSizeParameter.Value.ValueChanged += RealizationsSizeParameter_ValueChanged;
}
private void RealizationsSizeParameter_ValueChanged(object sender, EventArgs e) {
UpdateRealizations();
}
public override double Evaluate(Permutation tour, IRandom random) {
// abeham: Cache parameters in local variables for performance reasons
var realizations = Realizations;
var realizationsSize = RealizationsSize;
var useDistanceMatrix = UseDistanceMatrix;
var distanceMatrix = DistanceMatrix;
var distanceCalculator = DistanceCalculator;
var coordinates = Coordinates;
// Estimation-based evaluation, here without calculating variance for faster evaluation
var estimatedSum = 0.0;
for (var i = 0; i < realizations.Count; i++) {
int singleRealization = -1, firstNode = -1;
for (var j = 0; j < realizations[i].Length; j++) {
if (realizations[i][tour[j]]) {
if (singleRealization != -1) {
estimatedSum += useDistanceMatrix ? distanceMatrix[singleRealization, tour[j]] : distanceCalculator.Calculate(singleRealization, tour[j], coordinates);
} else {
firstNode = tour[j];
}
singleRealization = tour[j];
}
}
if (singleRealization != -1) {
estimatedSum += useDistanceMatrix ? distanceMatrix[singleRealization, firstNode] : distanceCalculator.Calculate(singleRealization, firstNode, coordinates);
}
}
return estimatedSum / realizationsSize;
}
///
/// An evaluate method that can be used if mean as well as variance should be calculated
///
/// The tour between all cities.
/// The distances between the cities.
/// A sample of realizations of the stochastic instance
/// The estimated variance will be returned in addition to the mean.
/// A vector with length two containing mean and variance.
public static double Evaluate(Permutation tour, DistanceMatrix distanceMatrix, ItemList realizations, out double variance) {
return Evaluate(tour, (a, b) => distanceMatrix[a, b], realizations, out variance);
}
///
/// An evaluate method that can be used if mean as well as variance should be calculated
///
/// The tour between all cities.
/// A func that accepts the index of two cities and returns the distance as a double.
/// A sample of realizations of the stochastic instance
/// The estimated variance will be returned in addition to the mean.
/// A vector with length two containing mean and variance.
public static double Evaluate(Permutation tour, Func distance, ItemList realizations, out double variance) {
// Estimation-based evaluation
var estimatedSum = 0.0;
var partialSums = new double[realizations.Count];
for (var i = 0; i < realizations.Count; i++) {
partialSums[i] = 0;
int singleRealization = -1, firstNode = -1;
for (var j = 0; j < realizations[i].Length; j++) {
if (realizations[i][tour[j]]) {
if (singleRealization != -1) {
partialSums[i] += distance(singleRealization, tour[j]);
} else {
firstNode = tour[j];
}
singleRealization = tour[j];
}
}
if (singleRealization != -1) {
partialSums[i] += distance(singleRealization, firstNode);
}
estimatedSum += partialSums[i];
}
var mean = estimatedSum / realizations.Count;
variance = 0.0;
for (var i = 0; i < realizations.Count; i++) {
variance += Math.Pow((partialSums[i] - mean), 2);
}
variance = variance / realizations.Count;
return mean;
}
public override void Load(PTSPData data) {
base.Load(data);
UpdateRealizations();
foreach (var op in Operators.OfType()) {
op.RealizationsParameter.ActualName = RealizationsParameter.Name;
}
}
private void UpdateRealizations() {
var realizations = new ItemList(RealizationsSize);
var rand = new MersenneTwister();
for (var i = 0; i < RealizationsSize; i++) {
var newRealization = new BoolArray(Probabilities.Length);
var countOnes = 0;
do {
countOnes = 0;
for (var j = 0; j < Probabilities.Length; j++) {
newRealization[j] = Probabilities[j] < rand.NextDouble();
if (newRealization[j]) countOnes++;
}
// only generate realizations with at least 4 cities visited
} while (countOnes < 4 && Probabilities.Length > 3);
realizations.Add(newRealization);
}
Realizations = realizations;
}
}
}