// Copyright 2010-2019 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Google.OrTools.Sat;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
///
/// This model solves a multicommodity mono-routing problem with
/// capacity constraints and a max usage cost structure. This means
/// that given a graph with capacity on edges, and a set of demands
/// (source, destination, traffic), the goal is to assign one unique
/// path for each demand such that the cost is minimized. The cost is
/// defined by the maximum ratio utilization (traffic/capacity) for all
/// arcs. There is also a penalty associated with an traffic of an arc
/// being above the comfort zone, 85% of the capacity by default.
/// Please note that constraint programming is well suited here because
/// we cannot have multiple active paths for a single demand.
/// Otherwise, a approach based on a linear solver is a better match.
/// A random problem generator is also included.
///
public class NetworkRoutingSat
{
private static int clients = 0; // Number of network clients nodes. If equal to zero, then all backbones nodes are also client nodes.
private static int backbones = 0; // "Number of backbone nodes"
private static int demands = 0; // "Number of network demands."
private static int trafficMin = 0; // "Min traffic of a demand."
private static int trafficMax = 0; // "Max traffic of a demand."
private static int minClientDegree = 0; //"Min number of connections from a client to the backbone."
private static int maxClientDegree = 0; //"Max number of connections from a client to the backbone."
private static int minBackboneDegree = 0; //"Min number of connections from a backbone node to the rest of the backbone nodes."
private static int maxBackboneDegree = 0; // "Max number of connections from a backbone node to the rest of the backbone nodes."
private static int maxCapacity = 0; //"Max traffic on any arc."
private static int fixedChargeCost = 0; //"Fixed charged cost when using an arc."
private static int seed = 0; //"Random seed"
private static double comfortZone = 0.85; // "Above this limit in 1/1000th, the link is said to be congested."
private static int extraHops = 6; // "When creating all paths for a demand, we look at paths with maximum length 'shortest path + extra_hops'"
private static int maxPaths = 1200; //"Max number of possible paths for a demand."
private static bool printModel = false; //"Print details of the model."
private static string parameters = ""; // "Sat parameters."
private const long kDisconnectedDistance = -1L;
static void Main(string[] args)
{
readArgs(args);
var builder = new NetworkRoutingDataBuilder();
var data = builder.BuildModelFromParameters(clients, backbones, demands, trafficMin, trafficMax,
minClientDegree, maxClientDegree, minBackboneDegree, maxBackboneDegree, maxCapacity, fixedChargeCost, seed);
var solver = new NetworkRoutingSolver();
solver.Init(data, extraHops, maxPaths);
var cost = solver.Solve();
Console.WriteLine($"Final cost = {cost}");
}
private static void readArgs(string[] args)
{
readInt(args, ref clients, nameof(clients));
readInt(args, ref backbones, nameof(backbones));
readInt(args, ref demands, nameof(demands));
readInt(args, ref trafficMin, nameof(trafficMin));
readInt(args, ref trafficMax, nameof(trafficMax));
readInt(args, ref minClientDegree, nameof(minClientDegree));
readInt(args, ref maxClientDegree, nameof(maxClientDegree));
readInt(args, ref minBackboneDegree, nameof(minBackboneDegree));
readInt(args, ref maxBackboneDegree, nameof(maxBackboneDegree));
readInt(args, ref maxCapacity, nameof(maxCapacity));
readInt(args, ref fixedChargeCost, nameof(fixedChargeCost));
readInt(args, ref seed, nameof(seed));
readDouble(args, ref comfortZone, nameof(comfortZone));
readInt(args, ref extraHops, nameof(extraHops));
readInt(args, ref maxPaths, nameof(maxPaths));
readBoolean(args, ref printModel, nameof(printModel));
readString(args, ref parameters, nameof(parameters));
}
private static void readDouble(string[] args, ref double setting, string arg)
{
var v = getArgValue(args, arg);
if (v.IsSet)
{
setting = Convert.ToDouble(v.Value);
}
}
private static void readInt(string[] args, ref int setting, string arg)
{
var v = getArgValue(args, arg);
if (v.IsSet)
{
setting = Convert.ToInt32(v.Value);
}
}
private static void readBoolean(string[] args, ref bool setting, string arg)
{
var v = getArgValue(args, arg);
if (v.IsSet)
{
setting = Convert.ToBoolean(v.Value);
}
}
private static void readString(string[] args, ref string setting, string arg)
{
var v = getArgValue(args, arg);
if (v.IsSet)
{
setting = v.Value;
}
}
private static (bool IsSet, string Value) getArgValue(string[] args, string arg)
{
string lookup = $"--{arg}=";
var item = args.FirstOrDefault(x => x.StartsWith(lookup));
if (string.IsNullOrEmpty(item))
{
return (false, string.Empty);
}
return (true, item.Replace(lookup, string.Empty));
}
///
/// Contains problem data. It assumes capacities are symmetrical:
/// (capacity(i->j) == capacity(j->i)).
/// Demands are not symmetrical.
///
public class NetworkRoutingData
{
private Dictionary<(int source, int destination), int> _arcs = new Dictionary<(int source, int destination), int>();
private Dictionary<(int node1, int node2), int> _demands = new Dictionary<(int node1, int node2), int>();
public int NumberOfNodes { get; set; } = -1;
public int NumberOfArcs
{
get { return _arcs.Count(); }
}
public int NumberOfDemands
{
get { return _demands.Count(); }
}
public int MaximumCapacity { get; set; } = -1;
public int FixedChargeCost { get; set; } = -1;
public string Name { get; set; } = string.Empty;
public void AddDemand(int source, int destination, int traffic)
{
var pair = (source, destination);
if(!_demands.ContainsKey(pair))
_demands.Add(pair, traffic);
}
public void AddArc(int node1, int node2, int capacity)
{
_arcs.Add((Math.Min(node1, node2), Math.Max(node1, node2)), capacity);
}
public int Demand(int source, int destination)
{
var pair = (source, destination);
if (_demands.TryGetValue(pair, out var demand))
return demand;
return 0;
}
public int Capacity(int node1, int node2)
{
var pair = (Math.Min(node1, node2), Math.Max(node1, node2));
if (_arcs.TryGetValue(pair, out var capacity))
return capacity;
return 0;
}
}
///
/// Random generator of problem. This generator creates a random
/// problem. This problem uses a special topology. There are
/// 'numBackbones' nodes and 'numClients' nodes. if 'numClients' is
/// null, then all backbones nodes are also client nodes. All traffic
/// originates and terminates in client nodes. Each client node is
/// connected to 'minClientDegree' - 'maxClientDegree' backbone
/// nodes. Each backbone node is connected to 'minBackboneDegree' -
/// 'maxBackboneDegree' other backbone nodes. There are 'numDemands'
/// demands, with a traffic between 'trafficMin' and 'trafficMax'.
/// Each arc has a capacity of 'maxCapacity'. Using an arc incurs a
/// fixed cost of 'fixedChargeCost'.
///
public class NetworkRoutingDataBuilder
{
private List> _network;
private List _degrees;
private Random _random;
public NetworkRoutingData BuildModelFromParameters(int numClients, int numBackbones,
int numDemands, int trafficMin,
int trafficMax, int minClientDegree,
int maxClientDegree, int minBackboneDegree,
int maxBackboneDegree, int maxCapacity,
int fixedChargeCost, int seed)
{
Debug.Assert(numBackbones >= 1);
Debug.Assert(numClients >= 0);
Debug.Assert(numDemands >= 1);
Debug.Assert(numDemands <= (numClients == 0 ? numBackbones * numBackbones : numClients * numBackbones));
Debug.Assert(maxClientDegree >= minClientDegree);
Debug.Assert(maxBackboneDegree >= minBackboneDegree);
Debug.Assert(trafficMax >= 1);
Debug.Assert(trafficMax >= trafficMin);
Debug.Assert(trafficMin >= 1);
Debug.Assert(maxBackboneDegree >= 2);
Debug.Assert(maxClientDegree >= 2);
Debug.Assert(maxClientDegree <= numBackbones);
Debug.Assert(maxBackboneDegree <= numBackbones);
Debug.Assert(maxCapacity >= 1);
int size = numBackbones + numClients;
initData(size, seed);
buildGraph(numClients, numBackbones, minClientDegree, maxClientDegree, minBackboneDegree, maxBackboneDegree);
NetworkRoutingData data = new NetworkRoutingData();
createDemands(numClients, numBackbones, numDemands, trafficMin, trafficMax, data);
fillData(numClients, numBackbones, numDemands, trafficMin, trafficMax, minClientDegree, maxClientDegree,
minBackboneDegree, maxBackboneDegree, maxCapacity, fixedChargeCost, seed, data);
return data;
}
private void initData(int size, int seed)
{
_network = new List>(size);
for (int i = 0; i < size; i++)
{
_network.Add(new List(size));
for (int j = 0; j < size; j++)
{
_network[i].Add(false);
}
}
_degrees = new List(size);
for (int i = 0; i < size; i++)
{
_degrees.Add(0);
}
_random = new Random(seed);
}
private void buildGraph(int numClients, int numBackbones, int minClientDegree,
int maxClientDegree, int minBackboneDegree, int maxBackboneDegree)
{
int size = numBackbones + numClients;
for (int i = 1; i < numBackbones; i++)
{
int j = randomUniform(i);
addEdge(i, j);
}
List notFull = new List();
HashSet toComplete = new HashSet();
for (int i = 0; i < numBackbones; i++)
{
if (_degrees[i] < minBackboneDegree)
{
toComplete.Add(i);
}
if (_degrees[i] < maxBackboneDegree)
{
notFull.Add(i);
}
}
while (toComplete.Any() && notFull.Count > 1)
{
int node1 = getNextToComplete(toComplete);
int node2 = node1;
while (node2 == node1 || _degrees[node2] >= maxBackboneDegree)
{
node2 = randomUniform(numBackbones);
}
addEdge(node1, node2);
if (_degrees[node1] >= minBackboneDegree)
{
toComplete.Remove(node1);
}
if (_degrees[node2] >= minBackboneDegree)
{
toComplete.Remove(node2);
}
if (_degrees[node1] >= maxBackboneDegree)
{
notFull.Remove(node1);
}
if (_degrees[node2] >= maxBackboneDegree)
{
notFull.Remove(node2);
}
}
// Then create the client nodes connected to the backbone nodes.
// If numClient is 0, then backbone nodes are also client nodes.
for (int i = numBackbones; i < size; i++)
{
int degree = randomInInterval(minClientDegree, maxClientDegree);
while (_degrees[i] < degree)
{
int j = randomUniform(numBackbones);
if (!_network[i][j])
{
addEdge(i, j);
}
}
}
}
private int getNextToComplete(HashSet toComplete)
{
return toComplete.Last();
}
private void createDemands(int numClients, int numBackbones, int numDemands,
int trafficMin, int trafficMax, NetworkRoutingData data)
{
while (data.NumberOfDemands < numDemands)
{
int source = randomClient(numClients, numBackbones);
int dest = source;
while (dest == source)
{
dest = randomClient(numClients, numBackbones);
}
int traffic = randomInInterval(trafficMin, trafficMax);
data.AddDemand(source, dest, traffic);
}
}
private void fillData(int numClients, int numBackbones, int numDemands,
int trafficMin, int trafficMax, int minClientDegree,
int maxClientDegree, int minBackboneDegree,
int maxBackboneDegree, int maxCapacity,
int fixedChargeCost, int seed,
NetworkRoutingData data)
{
int size = numBackbones + numClients;
string name = $"mp_c{numClients}_b{numBackbones}_d{numDemands}.t{trafficMin}-{trafficMax}.cd{minClientDegree}-{maxClientDegree}.bd{minBackboneDegree}-{maxBackboneDegree}.mc{maxCapacity}.fc{fixedChargeCost}.s{seed}";
data.Name = name;
data.NumberOfNodes = size;
int numArcs = 0;
for (int i = 0; i < size - 1; i++)
{
for (int j = i + 1; j < size; j++)
{
if (_network[i][j])
{
data.AddArc(i, j, maxCapacity);
numArcs++;
}
}
}
data.MaximumCapacity = maxCapacity;
data.FixedChargeCost = fixedChargeCost;
}
private void addEdge(int i, int j)
{
_degrees[i]++;
_degrees[j]++;
_network[i][j] = true;
_network[j][i] = true;
}
private int randomInInterval(int intervalMin, int intervalMax)
{
var p = randomUniform(intervalMax - intervalMin + 1) + intervalMin;
return p;
}
private int randomClient(int numClients, int numBackbones)
{
var p = (numClients == 0)
? randomUniform(numBackbones)
: randomUniform(numClients) + numBackbones;
return p;
}
private int randomUniform(int max)
{
var r = _random.Next(max);
return r;
}
}
[DebuggerDisplay("Source {Source} Destination {Destination} Traffic {Traffic}")]
public struct Demand
{
public Demand(int source, int destination, int traffic)
{
Source = source;
Destination = destination;
Traffic = traffic;
}
public int Source { get; }
public int Destination { get; }
public int Traffic { get; }
}
public class NetworkRoutingSolver
{
private List<(long source, long destination, int arcId)> _arcsData = new List<(long source, long destination, int arcId)>();
private List _arcCapacity = new List();
private List _demands = new List();
private List _allMinPathLengths = new List();
private List> _capacity;
private List>> _allPaths;
public int NumberOfNodes { get; private set; } = -1;
private int countArcs
{
get { return _arcsData.Count / 2; }
}
public void ComputeAllPathsForOneDemandAndOnePathLength(int demandIndex, int maxLength, int maxPaths)
{
// We search for paths of length exactly 'maxLength'.
CpModel cpModel = new CpModel();
var arcVars = new List();
var nodeVars = new List();
for (int i = 0; i < maxLength; i++)
{
nodeVars.Add(cpModel.NewIntVar(0, NumberOfNodes - 1, string.Empty));
}
for (int i = 0; i < maxLength - 1; i++)
{
arcVars.Add(cpModel.NewIntVar(-1, countArcs - 1, string.Empty));
}
var arcs = getArcsData();
for (int i = 0; i < maxLength - 1; i++)
{
var tmpVars = new List();
tmpVars.Add(nodeVars[i]);
tmpVars.Add(nodeVars[i + 1]);
tmpVars.Add(arcVars[i]);
var table = cpModel.AddAllowedAssignments(tmpVars, arcs);
}
var demand = _demands[demandIndex];
cpModel.Add(nodeVars[0] == demand.Source);
cpModel.Add(nodeVars[maxLength - 1] == demand.Destination);
cpModel.AddAllDifferent(arcVars);
cpModel.AddAllDifferent(nodeVars);
var solver = new CpSolver();
var solutionPrinter = new FeasibleSolutionChecker(demandIndex, ref _allPaths, maxLength, arcVars, maxPaths, nodeVars);
var status = solver.SearchAllSolutions(cpModel, solutionPrinter);
}
private long[,] getArcsData()
{
long[,] arcs = new long[_arcsData.Count, 3];
for (int i = 0; i < _arcsData.Count; i++)
{
var data = _arcsData[i];
arcs[i, 0] = data.source;
arcs[i, 1] = data.destination;
arcs[i, 2] = data.arcId;
}
return arcs;
}
public int ComputeAllPaths(int extraHops, int maxPaths)
{
int numPaths = 0;
for (int demandIndex = 0; demandIndex < _demands.Count; demandIndex++)
{
int minPathLength = _allMinPathLengths[demandIndex];
for (int maxLength = minPathLength + 1; maxLength <= minPathLength + extraHops+1; maxLength++)
{
ComputeAllPathsForOneDemandAndOnePathLength(demandIndex, maxLength, maxPaths);
if (_allPaths[demandIndex].Count >= maxPaths)
break;
}
numPaths += _allPaths[demandIndex].Count;
}
return numPaths;
}
public void AddArcData(long source, long destination, int arcId)
{
_arcsData.Add((source, destination, arcId));
}
public void InitArcInfo(NetworkRoutingData data)
{
int numArcs = data.NumberOfArcs;
_capacity = new List>(NumberOfNodes);
for (int nodeIndex = 0; nodeIndex < NumberOfNodes; nodeIndex++)
{
_capacity.Add(new List(NumberOfNodes));
for (int i = 0; i < NumberOfNodes; i++)
{
_capacity[nodeIndex].Add(0);
}
}
int arcId = 0;
for (int i = 0; i < NumberOfNodes - 1; i++)
{
for (int j = i + 1; j < NumberOfNodes; j++)
{
int capacity = data.Capacity(i, j);
if (capacity > 0)
{
AddArcData(i, j, arcId);
AddArcData(j, i, arcId);
arcId++;
_arcCapacity.Add(capacity);
_capacity[i][j] = capacity;
_capacity[j][i] = capacity;
if (printModel)
{
Console.WriteLine($"Arc {i} <-> {j} with capacity {capacity}");
}
}
}
}
Debug.Assert(arcId == numArcs);
}
public int InitDemandInfo(NetworkRoutingData data)
{
int numDemands = data.NumberOfDemands;
int totalDemand = 0;
for (int i = 0; i < NumberOfNodes; i++)
{
for (int j = 0; j < NumberOfNodes; j++)
{
int traffic = data.Demand(i, j);
if (traffic > 0)
{
_demands.Add(new Demand(i, j, traffic));
totalDemand += traffic;
}
}
}
Debug.Assert(numDemands == _demands.Count);
return totalDemand;
}
public long InitShortestPaths(NetworkRoutingData data)
{
int numDemands = data.NumberOfDemands;
long totalCumulatedTraffic = 0L;
_allMinPathLengths.Clear();
var paths = new List();
for (int demandIndex = 0; demandIndex < numDemands; demandIndex++)
{
paths.Clear();
var demand = _demands[demandIndex];
var r = DijkstraShortestPath(NumberOfNodes, demand.Source, demand.Destination,
((int x, int y) p) => hasArc(p.x, p.y), kDisconnectedDistance, paths);
_allMinPathLengths.Add(paths.Count - 1);
var minPathLength = _allMinPathLengths[demandIndex];
totalCumulatedTraffic += minPathLength * demand.Traffic;
}
return totalCumulatedTraffic;
}
public int InitPaths(NetworkRoutingData data, int extraHops, int maxPaths)
{
var numDemands = data.NumberOfDemands;
Console.WriteLine("Computing all possible paths ");
Console.WriteLine($" - extra hops = {extraHops}");
Console.WriteLine($" - max paths per demand = {maxPaths}");
_allPaths =new List>>(numDemands);
var numPaths = ComputeAllPaths(extraHops, maxPaths);
for (int demandIndex = 0; demandIndex < numDemands; demandIndex++)
{
var demand = _demands[demandIndex];
Console.WriteLine($"Demand from {demand.Source} to {demand.Destination} with traffic {demand.Traffic}, amd {_allPaths[demandIndex].Count} possible paths.");
}
return numPaths;
}
public void Init(NetworkRoutingData data, int extraHops, int maxPaths)
{
Console.WriteLine($"Model {data.Name}");
NumberOfNodes = data.NumberOfNodes;
var numArcs = data.NumberOfArcs;
var numDemands = data.NumberOfDemands;
InitArcInfo(data);
var totalDemand = InitDemandInfo(data);
var totalAccumulatedTraffic = InitShortestPaths(data);
var numPaths = InitPaths(data, extraHops, maxPaths);
Console.WriteLine("Model created:");
Console.WriteLine($" - {NumberOfNodes} nodes");
Console.WriteLine($" - {numArcs} arcs");
Console.WriteLine($" - {numDemands} demands");
Console.WriteLine($" - a total traffic of {totalDemand}");
Console.WriteLine($" - a minimum cumulated traffic of {totalAccumulatedTraffic}");
Console.WriteLine($" - {numPaths} possible paths for all demands");
}
private long hasArc(int i, int j)
{
if (_capacity[i][j] > 0)
return 1;
else
return kDisconnectedDistance;
}
public long Solve()
{
Console.WriteLine("Solving model");
var numDemands = _demands.Count;
var numArcs = countArcs;
CpModel cpModel = new CpModel();
var pathVars = new List>(numDemands);
for (int demandIndex = 0; demandIndex < numDemands; demandIndex++)
{
pathVars.Add(new List());
for (int arc = 0; arc < numArcs; arc++)
{
pathVars[demandIndex].Add(cpModel.NewBoolVar(""));
}
long[,] tuples = new long[_allPaths[demandIndex].Count, numArcs];
int pathCount = 0;
foreach (var set in _allPaths[demandIndex])
{
foreach (var arc in set)
{
tuples[pathCount, arc] = 1;
}
pathCount++;
}
var pathCt = cpModel.AddAllowedAssignments(pathVars[demandIndex], tuples);
}
var trafficVars = new List(numArcs);
var normalizedTrafficVars = new List(numArcs);
var comfortableTrafficVars = new List(numArcs);
long maxNormalizedTraffic = 0;
for (int arcIndex = 0; arcIndex < numArcs; arcIndex++)
{
long sumOfTraffic = 0;
var vars = new List();
var traffics = new List();
for (int i = 0; i < pathVars.Count; i++)
{
sumOfTraffic += _demands[i].Traffic;
vars.Add(pathVars[i][arcIndex]);
traffics.Add(_demands[i].Traffic);
}
var sum = LinearExpr.ScalProd(vars, traffics);
var trafficVar = cpModel.NewIntVar(0, sumOfTraffic, $"trafficVar{arcIndex}");
trafficVars.Add(trafficVar);
cpModel.Add(sum == trafficVar);
var capacity = _arcCapacity[arcIndex];
var scaledTraffic = cpModel.NewIntVar(0, sumOfTraffic * 1000, $"scaledTrafficVar{arcIndex}");
var scaledTrafficVar = trafficVar * 1000;
cpModel.Add(scaledTrafficVar == scaledTraffic);
var normalizedTraffic =
cpModel.NewIntVar(0, sumOfTraffic * 1000 / capacity, $"normalizedTraffic{arcIndex}");
maxNormalizedTraffic = Math.Max(maxNormalizedTraffic, sumOfTraffic * 1000 / capacity);
cpModel.AddDivisionEquality(normalizedTraffic, scaledTraffic, cpModel.NewConstant(capacity));
normalizedTrafficVars.Add(normalizedTraffic);
var comfort = cpModel.NewBoolVar($"comfort{arcIndex}");
var safeCapacity = (long)(capacity * comfortZone);
cpModel.Add(trafficVar > safeCapacity).OnlyEnforceIf(comfort);
cpModel.Add(trafficVar <=safeCapacity).OnlyEnforceIf(comfort.Not());
comfortableTrafficVars.Add(comfort);
}
var maxUsageCost = cpModel.NewIntVar(0, maxNormalizedTraffic, "maxUsageCost");
cpModel.AddMaxEquality(maxUsageCost, normalizedTrafficVars);
var obj = new List() {maxUsageCost};
obj.AddRange(comfortableTrafficVars);
cpModel.Minimize(LinearExpr.Sum(obj));
CpSolver solver = new CpSolver();
solver.StringParameters = parameters;
CpSolverStatus status = solver.SearchAllSolutions(cpModel,
new FeasibleSolutionChecker2(maxUsageCost, comfortableTrafficVars, trafficVars));
return (long)solver.ObjectiveValue;
}
}
private class DijkstraSP
{
private const long kInfinity = long.MaxValue / 2;
private readonly Func<(int, int), long> _graph;
private readonly int[] _predecessor;
private readonly List _elements;
private readonly AdjustablePriorityQueue _frontier;
private readonly List _notVisited = new List();
private readonly List _addedToFrontier = new List();
public DijkstraSP(int nodeCount, int startNode, Func<(int,int), long> graph, long disconnectedDistance)
{
NodeCount = nodeCount;
StartNode = startNode;
this._graph = graph;
DisconnectedDistance = disconnectedDistance;
_predecessor = new int[nodeCount];
_elements = new List(nodeCount);
_frontier = new AdjustablePriorityQueue();
}
public int NodeCount { get; }
public int StartNode { get; }
public long DisconnectedDistance { get; }
public bool ShortestPath(int endNode, List nodes)
{
initialize();
bool found = false;
while (!_frontier.IsEmpty)
{
long distance;
int node = selectClosestNode(out distance);
if (distance == kInfinity)
{
found = false;
break;
}
else if (node == endNode)
{
found = true;
break;
}
update(node);
}
if (found)
{
findPath(endNode, nodes);
}
return found;
}
private void initialize()
{
for (int i = 0; i < NodeCount; i++)
{
_elements.Add(new Element {Node = i});
if (i == StartNode)
{
_predecessor[i] = -1;
_elements[i].Distance = 0;
_frontier.Add(_elements[i]);
}
else
{
_elements[i].Distance = kInfinity;
_predecessor[i] = StartNode;
_notVisited.Add(i);
}
}
}
private int selectClosestNode(out long distance)
{
var node = _frontier.Top().Node;
distance = _frontier.Top().Distance;
_frontier.Pop();
_notVisited.Remove(node);
_addedToFrontier.Remove(node);
return node;
}
private void update(int node)
{
foreach (var otherNode in _notVisited)
{
var graphNode = _graph((node, otherNode));
if (graphNode != DisconnectedDistance)
{
if (!_addedToFrontier.Contains(otherNode))
{
_frontier.Add(_elements[otherNode]);
_addedToFrontier.Add(otherNode);
}
var otherDistance = _elements[node].Distance + graphNode;
if (_elements[otherNode].Distance > otherDistance)
{
_elements[otherNode].Distance = otherDistance;
_frontier.NoteChangedPriority(_elements[otherNode]);
_predecessor[otherNode] = node;
}
}
}
}
private void findPath(int dest, List nodes)
{
var j = dest;
nodes.Add(j);
while (_predecessor[j] != -1)
{
nodes.Add(_predecessor[j]);
j = _predecessor[j];
}
}
}
public static bool DijkstraShortestPath(int nodeCount, int startNode, int endNode, Func<(int, int), long> graph,
long disconnectedDistance, List nodes)
{
DijkstraSP bf = new DijkstraSP(nodeCount, startNode, graph, disconnectedDistance);
return bf.ShortestPath(endNode, nodes);
}
[DebuggerDisplay("Node = {Node}, HeapIndex = {HeapIndex}, Distance = {Distance}")]
private class Element : IHasHeapIndex, IComparable
{
public int HeapIndex { get; set; } = -1;
public long Distance { get; set; } = 0;
public int Node { get; set; } = -1;
public int CompareTo(Element other)
{
if (this.Distance > other.Distance)
return -1;
if (this.Distance < other.Distance)
return 1;
return 0;
}
}
private class AdjustablePriorityQueue where T: class, IHasHeapIndex, IComparable
{
private readonly List _elems = new List();
public void Add(T val)
{
_elems.Add(val);
adjustUpwards(_elems.Count - 1);
}
public void Remove(T val)
{
var i = val.HeapIndex;
if (i == _elems.Count - 1)
{
_elems.RemoveAt(_elems.Count - 1);
return;
}
_elems[i] = _elems.Last();
_elems[i].HeapIndex = i;
_elems.RemoveAt(_elems.Count - 1);
NoteChangedPriority(_elems[i]);
}
public bool Contains(T val)
{
var i = val.HeapIndex;
if (i < 0 || i >= _elems.Count || _elems[i].CompareTo(val) != 0)
return false;
return true;
}
public T Top()
{
return _elems[0];
}
public void Pop()
{
Remove(Top());
}
public int Size()
{
return _elems.Count;
}
public bool IsEmpty
{
get { return !_elems.Any(); }
}
public void Clear()
{
_elems.Clear();
}
public void CheckValid()
{
for (int i = 0; i < _elems.Count; i++)
{
var leftChild = 1 + 2 * i;
if (leftChild < _elems.Count)
{
var compare = _elems[i].CompareTo(_elems[leftChild]);
Debug.Assert(compare >=0);
}
int rightChild = leftChild + 1;
if (rightChild < _elems.Count)
{
var compare = _elems[i].CompareTo(_elems[rightChild]);
Debug.Assert(compare >= 0);
}
}
}
public void NoteChangedPriority(T val)
{
if (_elems.Count == 0)
return;
var i = val.HeapIndex;
var parent = (i - 1) / 2;
if (_elems[parent].CompareTo(val) == -1)
{
adjustUpwards(i);
}
else
{
adjustDownwards(i);
}
}
private void adjustUpwards(int i)
{
var t = _elems[i];
while (i > 0)
{
var parent = (i - 1) / 2;
if (_elems[parent].CompareTo(t) != -1)
{
break;
}
_elems[i] = _elems[parent];
_elems[i].HeapIndex = i;
i = parent;
}
_elems[i] = t;
t.HeapIndex = i;
}
private void adjustDownwards(int i)
{
var t = _elems[i];
while (true)
{
var leftChild = 1 + 2 * i;
if (leftChild >= _elems.Count)
{
break;
}
var rightChild = leftChild + 1;
var next = (rightChild < _elems.Count && _elems[leftChild].CompareTo(_elems[rightChild]) == -1)
? rightChild
: leftChild;
if (t.CompareTo(_elems[next]) != -1)
{
break;
}
_elems[i] = _elems[next];
_elems[i].HeapIndex = i;
i = next;
}
_elems[i] = t;
t.HeapIndex = i;
}
}
public interface IHasHeapIndex
{
int HeapIndex { get; set; }
}
private class FeasibleSolutionChecker : CpSolverSolutionCallback
{
public FeasibleSolutionChecker(int demandIndex, ref List>> allPaths, int maxLength, List arcVars, int maxPaths, List nodeVars)
{
DemandIndex = demandIndex;
AllPaths = allPaths;
MaxLength = maxLength;
ArcVars = arcVars;
MaxPaths = maxPaths;
NodeVars = nodeVars;
}
public int DemandIndex { get; }
public List>> AllPaths { get; }
public int MaxLength { get; }
public List ArcVars { get; }
public int MaxPaths { get; }
public List NodeVars { get; }
public override void OnSolutionCallback()
{
if(AllPaths.Count < DemandIndex + 1)
AllPaths.Add(new List>());
int pathId = AllPaths[DemandIndex].Count;
AllPaths[DemandIndex].Add(new HashSet());
for (int i = 0; i < MaxLength - 1; i++)
{
int arc = (int) this.SolutionIntegerValue(ArcVars[i].GetIndex());
AllPaths[DemandIndex][pathId].Add(arc);
}
if (AllPaths[DemandIndex].Count() >= MaxPaths)
{
StopSearch();
}
}
}
private class FeasibleSolutionChecker2 : CpSolverSolutionCallback
{
public IntVar MaxUsageCost { get; }
public List ComfortableTrafficVars { get; }
public List TrafficVars { get; }
private int _numSolutions = 0;
public FeasibleSolutionChecker2(IntVar maxUsageCost, List comfortableTrafficVars, List trafficVars)
{
MaxUsageCost = maxUsageCost;
ComfortableTrafficVars = comfortableTrafficVars;
TrafficVars = trafficVars;
}
public override void OnSolutionCallback()
{
Console.WriteLine($"Solution {_numSolutions}");
var percent = SolutionIntegerValue(MaxUsageCost.GetIndex()) / 10.0;
int numNonComfortableArcs = 0;
foreach (var comfort in ComfortableTrafficVars)
{
numNonComfortableArcs += SolutionBooleanValue(comfort.GetIndex()) ? 1 : 0;
}
if (numNonComfortableArcs > 0)
{
Console.WriteLine($"*** Found a solution with a max usage of {percent}%, and {numNonComfortableArcs} links above the comfort zone");
}
else
{
Console.WriteLine($"*** Found a solution with a max usage of {percent}%");
}
_numSolutions++;
}
}
}