All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.spanning.EsauWilliamsCapacitatedMinimumSpanningTree Maven / Gradle / Ivy

/*
 * (C) Copyright 2018-2021, by Christoph Grüne and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.spanning;

import org.jgrapht.*;
import org.jgrapht.alg.util.*;

import java.util.*;

/**
 * Implementation of a randomized version of the Esau-Williams heuristic, a greedy randomized
 * adaptive search heuristic (GRASP) for the capacitated minimum spanning tree (CMST) problem. It
 * calculates a suboptimal CMST. The original version can be found in L. R. Esau and K. C. Williams.
 * 1966. On teleprocessing system design: part II a method for approximating the optimal network.
 * IBM Syst. J. 5, 3 (September 1966), 142-147. DOI=http://dx.doi.org/10.1147/sj.53.0142 This
 * implementation runs in polynomial time O(|V|^3).
 * 

* This implementation is a randomized version described in Ahuja, Ravindra K., Orlin, James B., and * Sharma, Dushyant, (1998). New neighborhood search structures for the capacitated minimum spanning * tree problem, No WP 4040-98. Working papers, Massachusetts Institute of Technology (MIT), Sloan * School of Management. *

* This version runs in polynomial time dependent on the number of considered operations per * iteration numberOfOperationsParameter (denoted by p), such that runs is in $O(|V|^3 * + p|V|) = O(|V|^3)$ since $p \leq |V|$. *

* A Capacitated Minimum * Spanning Tree (CMST) is a rooted minimal cost spanning tree that satisfies the capacity * constrained on all trees that are connected to the designated root. The problem is NP-hard. * * @param the vertex type * @param the edge type * * @author Christoph Grüne * @since July 12, 2018 */ public class EsauWilliamsCapacitatedMinimumSpanningTree extends AbstractCapacitatedMinimumSpanningTree { /** * the number of the most profitable operations for every iteration considered in the procedure. */ private final int numberOfOperationsParameter; /** * contains whether the algorithm was executed */ private boolean isAlgorithmExecuted; /** * Constructs an Esau-Williams GRASP algorithm instance. * * @param graph the graph * @param root the root of the CMST * @param capacity the capacity constraint of the CMST * @param weights the weights of the vertices * @param numberOfOperationsParameter the parameter how many best vertices are considered in the * procedure */ public EsauWilliamsCapacitatedMinimumSpanningTree( Graph graph, V root, double capacity, Map weights, int numberOfOperationsParameter) { super(graph, root, capacity, weights); this.numberOfOperationsParameter = numberOfOperationsParameter; this.isAlgorithmExecuted = false; } /** * {@inheritDoc} *

* Returns a capacitated spanning tree computed by the Esau-Williams algorithm. */ @Override public CapacitatedSpanningTree getCapacitatedSpanningTree() { if (isAlgorithmExecuted) { return bestSolution.calculateResultingSpanningTree(); } bestSolution = getSolution(); CapacitatedSpanningTree cmst = bestSolution.calculateResultingSpanningTree(); isAlgorithmExecuted = true; if (!cmst.isCapacitatedSpanningTree(graph, root, capacity, demands)) { throw new IllegalArgumentException( "This graph does not have a capacitated minimum spanning tree with the given capacity and demands."); } return cmst; } /** * Calculates a partition representation of the capacitated spanning tree. With that, it is * possible to calculate the to the partition corresponding capacitated spanning tree in * polynomial time by calculating the MSTs. The labels of the partition that are returned are * non-negative. * * @return a representation of the partition of the capacitated spanning tree that has * non-negative labels. */ protected CapacitatedSpanningTreeSolutionRepresentation getSolution() { /* * labeling of the improvement graph vertices. There are two vertices in the improvement * graph for every vertex in the input graph: the vertex indicating the vertex itself and * the vertex indicating the subtree. */ Map labels = new HashMap<>(); /* * the implicit partition defined by the subtrees */ Map, Double>> partition = new HashMap<>(); /* * initialize labels and partitions by assigning every vertex to a new part and create * solution representation */ int counter = 0; for (V v : graph.vertexSet()) { if (v != root) { labels.put(v, counter); Set currentPart = new HashSet<>(); currentPart.add(v); partition.put(counter, Pair.of(currentPart, demands.get(v))); counter++; } } /* * construct a new solution representation with the initialized labels and partition */ bestSolution = new CapacitatedSpanningTreeSolutionRepresentation(labels, partition); /* * map that contains the current savings for all vertices */ Map savings = new HashMap<>(); /* * map that contains the current closest vertex for all vertices */ Map closestVertex = new HashMap<>(); /* * map that contains all labels of partition the vertex cannot be assigned because the * capacity would be exceeded */ Map> restrictionMap = new HashMap<>(); /* * map that contains the vertex that is nearest to the root vertex for all labels of the * partition */ Map shortestGate = new HashMap<>(); /* * set of vertices that have to be considered in the current iteration */ Set vertices = new HashSet<>(graph.vertexSet()); vertices.remove(root); while (true) { for (Iterator it = vertices.iterator(); it.hasNext();) { V v = it.next(); V closestVertexToV = calculateClosestVertex(v, restrictionMap, shortestGate); if (closestVertexToV == null) { // there is not valid closest vertex to connect with, i.e. v will not be // connected to any vertex it.remove(); savings.remove(v); continue; } // store closest vertex to v1 closestVertex.put(v, closestVertexToV); // store the maximum saving and the corresponding vertex savings .put( v, getDistance(shortestGate.getOrDefault(bestSolution.getLabel(v), v), root) - getDistance(v, closestVertexToV)); } // calculate list of best operations LinkedList bestVertices = getListOfBestOptions(savings); if (!bestVertices.isEmpty()) { V vertexToMove = bestVertices.get((int) (Math.random() * bestVertices.size())); // update shortestGate Integer labelOfVertexToMove = bestSolution.getLabel(vertexToMove); V closestMoveVertex = closestVertex.get(vertexToMove); Integer labelOfClosestMoveVertex = bestSolution.getLabel(closestMoveVertex); V shortestGate1 = shortestGate.getOrDefault(labelOfVertexToMove, vertexToMove); V shortestGate2 = shortestGate.getOrDefault(labelOfClosestMoveVertex, closestMoveVertex); /* * Do improving move. The case distinction is important such that the the * restriction map uses minimal space. If the restriction map contains the label of * the part with bigger weight, i.e. the vertex cannot be connected to this part, * the label is still correct and is not the label of the old part, which will be * deleted by the move operation. */ if (bestSolution.getPartitionWeight(labelOfVertexToMove) < bestSolution .getPartitionWeight(labelOfClosestMoveVertex)) { bestSolution .moveVertices( bestSolution.getPartitionSet(labelOfVertexToMove), labelOfVertexToMove, labelOfClosestMoveVertex); if (getDistance(shortestGate1, root) < getDistance(shortestGate2, root)) { shortestGate.put(labelOfClosestMoveVertex, shortestGate1); } else { shortestGate.put(labelOfClosestMoveVertex, shortestGate2); } } else { bestSolution .moveVertices( bestSolution.getPartitionSet(labelOfClosestMoveVertex), labelOfClosestMoveVertex, labelOfVertexToMove); if (getDistance(shortestGate1, root) < getDistance(shortestGate2, root)) { shortestGate.put(labelOfVertexToMove, shortestGate1); } else { shortestGate.put(labelOfVertexToMove, shortestGate2); } } } else { break; } } CapacitatedSpanningTreeSolutionRepresentation result = new CapacitatedSpanningTreeSolutionRepresentation(labels, partition); result.cleanUp(); Set labelSet = new HashSet<>(result.getLabels()); for (Integer label : labelSet) { result.partitionSubtreesOfSubset(result.getPartitionSet(label), label); } return result; } /** * Returns the list of the best options as stored in savings. * * @param savings the savings calculated in the algorithm (see getSolution()) * @return the list of the numberOfOperationsParameter best options */ private LinkedList getListOfBestOptions(Map savings) { LinkedList bestVertices = new LinkedList<>(); for (Map.Entry entry : savings.entrySet()) { /* * insert current tradeOffFunction entry at the position such that the list is order by * the tradeOff and the size of the list is at most numberOfOperationsParameter */ int position = 0; for (V v : bestVertices) { if (savings.get(v) < entry.getValue()) { break; } position++; } if (bestVertices.size() == numberOfOperationsParameter) { if (position < bestVertices.size()) { bestVertices.removeLast(); bestVertices.add(position, entry.getKey()); } } else { bestVertices.addLast(entry.getKey()); } } return bestVertices; } /** * Calculates the closest vertex to vertex such that the connection of * vertex to the subtree of the closest vertex does not violate the capacity * constraint and the savings are positive. Otherwise null is returned. * * @param vertex the vertex to find a valid closest vertex for * @param restrictionMap the set of labels of sets of the partition, in which the capacity * constraint is violated. * @return the closest valid vertex and null, if no valid vertex exists */ private V calculateClosestVertex( V vertex, Map> restrictionMap, Map shortestGate) { V closestVertexToV1 = null; double distanceToRoot; V shortestGateOfV = shortestGate.get(bestSolution.getLabel(vertex)); if (shortestGateOfV != null) { distanceToRoot = getDistance(shortestGateOfV, root); } else { distanceToRoot = getDistance(vertex, root); } // calculate closest vertex to v1 for (Integer label : bestSolution.getLabels()) { Set restrictionSet = restrictionMap.get(vertex); if (restrictionSet == null || !restrictionSet.contains(label)) { Set part = bestSolution.getPartitionSet(label); if (!part.contains(vertex)) { for (V v2 : part) { if (graph.containsEdge(vertex, v2)) { double newWeight = bestSolution .getPartitionWeight(bestSolution.getLabel(v2)) + bestSolution.getPartitionWeight(bestSolution.getLabel(vertex)); if (newWeight <= capacity) { double currentEdgeWeight = getDistance(vertex, v2); if (currentEdgeWeight < distanceToRoot) { closestVertexToV1 = v2; distanceToRoot = currentEdgeWeight; } } else { /* * the capacity would be exceeded if the vertex would be assigned to * this part, so add the part to the restricted parts */ Set restriction = restrictionMap.computeIfAbsent(vertex, k -> new HashSet<>()); restriction.add(bestSolution.getLabel(v2)); break; } } } } } } return closestVertexToV1; } private double getDistance(V v1, V v2) { E e = graph.getEdge(v1, v2); if (e == null) { return Double.MAX_VALUE; } return graph.getEdgeWeight(e); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy