#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.Threading;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.IntegerVectorEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using localsolver;
namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Algorithms.LocalSolverNet {
[Item("LocalSolver Binary (GQAP)", "LocalSolver algorithm solving the GQAP using 0-1 decision variables")]
[StorableClass]
[Creatable(CreatableAttribute.Categories.Algorithms)]
public sealed class GQAPBinarySolver : ContextAlgorithm {
public override bool SupportsPause {
get { return false; }
}
public override Type ProblemType {
get { return typeof(GQAP); }
}
public new GQAP Problem {
get { return (GQAP)base.Problem; }
set { base.Problem = value; }
}
// LS Program variables
private LSExpression[][] x;
private LSExpression[] equipmentsOnLocations;
private LSExpression obj;
private LocalSolver localSolver;
[StorableConstructor]
private GQAPBinarySolver(bool deserializing) : base(deserializing) { }
private GQAPBinarySolver(GQAPBinarySolver original, Cloner cloner)
: base(original, cloner) {
}
public GQAPBinarySolver() {
Problem = new GQAP();
MaximumEvaluationsParameter.Hidden = true;
MaximumIterationsParameter.Hidden = true;
}
public override IDeepCloneable Clone(Cloner cloner) {
return new GQAPBinarySolver(this, cloner);
}
private double prevObj;
private DateTime lastUpdate;
private CancellationToken token;
private void LocalSolverOnIterationTicked(LocalSolver ls, LSCallbackType type) {
IResult result;
Context.Iterations++;
if ((DateTime.UtcNow - lastUpdate) > TimeSpan.FromSeconds(1)) {
if (Results.TryGetValue("Iterations", out result))
((IntValue)result.Value).Value = Context.Iterations;
else Results.Add(new Result("Iterations", new IntValue(Context.Iterations)));
lastUpdate = DateTime.UtcNow;
}
if (token.IsCancellationRequested) localSolver.Stop();
var curObj = obj.GetDoubleValue();
if (curObj >= prevObj) return;
UpdateSolution(curObj);
Context.RunOperator(Analyzer, CancellationToken.None);
if (StoppingCriterion()) localSolver.Stop();
}
private void UpdateSolution(double obj) {
IResult result;
prevObj = obj;
Context.BestQuality = obj;
if (Results.TryGetValue("BestQuality", out result))
((DoubleValue)result.Value).Value = Context.BestQuality;
else Results.Add(new Result("BestQuality", new DoubleValue(Context.BestQuality)));
var locations = Problem.ProblemInstance.Capacities.Length;
var best = new int[Problem.ProblemInstance.Demands.Length];
for (var i = 0; i < best.Length; i++) {
for (var j = 0; j < locations; j++) {
if (x[i][j].GetIntValue() == 1) {
best[i] = j;
break;
}
}
}
var bestVec = new IntegerVector(best);
var eval = Problem.ProblemInstance.Evaluate(bestVec);
Context.BestSolution = new GQAPSolution(bestVec, eval);
var scope = Context.ToScope(new GQAPSolution(new IntegerVector(best), (Evaluation)eval.Clone()), Problem.ProblemInstance.ToSingleObjective(eval));
Context.ReplaceIncumbent(scope);
if (Results.TryGetValue("BestSolution", out result))
result.Value = Context.BestSolution;
else Results.Add(new Result("BestSolution", Context.BestSolution));
}
protected override void Initialize(CancellationToken cancellationToken) {
base.Initialize(cancellationToken);
prevObj = double.MaxValue;
}
protected override void Run(CancellationToken cancellationToken) {
base.Run(cancellationToken);
token = cancellationToken;
lastUpdate = DateTime.UtcNow.AddSeconds(-1);
localSolver = new LocalSolver();
// Declares the optimization model
LSModel model = localSolver.GetModel();
var data = Problem.ProblemInstance;
// x[f,l] = 1 if equipments f is on location l, 0 otherwise
x = new LSExpression[data.Demands.Length][];
for (int f = 0; f < data.Demands.Length; f++) {
x[f] = new LSExpression[data.Capacities.Length];
for (int l = 0; l < data.Capacities.Length; l++) {
x[f][l] = model.Bool();
}
}
// All equipments are installed in exactly 1 location
for (int f = 0; f < data.Demands.Length; f++) {
LSExpression nbLocationsAssigned = model.Sum();
for (int l = 0; l < data.Capacities.Length; l++) {
nbLocationsAssigned.AddOperand(x[f][l]);
}
model.Constraint(nbLocationsAssigned == 1);
}
// All locations contain not more equipments than there is capacity for
for (int l = 0; l < data.Capacities.Length; l++) {
LSExpression assignedDemand = model.Sum();
for (int f = 0; f < data.Demands.Length; f++) {
assignedDemand.AddOperand(x[f][l] * data.Demands[f]);
}
model.Constraint(assignedDemand <= data.Capacities[l]);
}
// Index of the assigned location of equipment f
equipmentsOnLocations = new LSExpression[data.Demands.Length];
for (int f = 0; f < data.Demands.Length; f++) {
equipmentsOnLocations[f] = model.Sum();
for (int l = 0; l < data.Capacities.Length; l++) {
equipmentsOnLocations[f].AddOperand(l * x[f][l]);
}
}
// Create distances as an array to be accessed by an at operator
var distancesJagged = new double[data.Capacities.Length][];
for (var i = 0; i < data.Capacities.Length; i++) {
distancesJagged[i] = new double[data.Capacities.Length];
for (var j = 0; j < data.Capacities.Length; j++)
distancesJagged[i][j] = data.Distances[i, j];
}
var installJagged = new double[data.Demands.Length][];
for (var i = 0; i < data.Demands.Length; i++) {
installJagged[i] = new double[data.Capacities.Length];
for (var j = 0; j < data.Capacities.Length; j++)
installJagged[i][j] = data.InstallationCosts[i, j];
}
LSExpression distancesArray = model.Array(distancesJagged);
LSExpression installCostsArray = model.Array(installJagged);
// Minimize the sum of product distance*flow
obj = model.Sum();
for (int f1 = 0; f1 < data.Demands.Length; f1++) {
for (int f2 = 0; f2 < data.Demands.Length; f2++) {
obj.AddOperand(data.TransportationCosts * data.Weights[f1, f2] * distancesArray[equipmentsOnLocations[f1], equipmentsOnLocations[f2]]);
}
obj.AddOperand(installCostsArray[f1, equipmentsOnLocations[f1]]);
}
model.Minimize(obj);
try {
model.Close();
// Parameterizes the solver.
LSPhase phase = localSolver.CreatePhase();
phase.SetTimeLimit((int)Math.Ceiling(MaximumRuntime.TotalSeconds));
localSolver.AddCallback(LSCallbackType.IterationTicked, LocalSolverOnIterationTicked);
localSolver.Solve();
var curObj = obj.GetDoubleValue();
if (curObj < prevObj)
UpdateSolution(curObj);
localSolver.RemoveCallback(LSCallbackType.IterationTicked, LocalSolverOnIterationTicked);
} finally {
localSolver.Dispose();
}
Context.RunOperator(Analyzer, CancellationToken.None);
}
}
}