com.salesforce.jgrapht.alg.spanning.AbstractCapacitatedMinimumSpanningTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of AptSpringProcessor Show documentation
Show all versions of AptSpringProcessor Show documentation
This project contains the apt processor that implements all the checks enumerated in @Verify. It is a self contained, and
shaded jar.
The newest version!
/*
* (C) Copyright 2018-2018, 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 com.salesforce.jgrapht.alg.spanning;
import com.salesforce.jgrapht.*;
import com.salesforce.jgrapht.alg.connectivity.*;
import com.salesforce.jgrapht.alg.interfaces.*;
import com.salesforce.jgrapht.alg.util.*;
import com.salesforce.jgrapht.graph.*;
import com.salesforce.jgrapht.traverse.*;
import com.salesforce.jgrapht.util.*;
import java.util.*;
/**
* This is an abstract class for capacitated minimum spanning tree algorithms. This class manages
* the basic instance information and a solution representation {see
* CapacitatedSpanningTreeSolutionRepresentation} for a capacitated spanning tree.
*
* @param the vertex type
* @param the edge type
*
* @author Christoph Grüne
* @since July 18, 2018
*/
public abstract class AbstractCapacitatedMinimumSpanningTree
implements
CapacitatedSpanningTreeAlgorithm
{
/**
* the input graph.
*/
protected final Graph graph;
/**
* the designated root of the CMST.
*/
protected final V root;
/**
* the maximal capacity for each subtree.
*/
protected final double capacity;
/**
* the demand function over all vertices.
*/
protected final Map demands;
/**
* representation of the solution
*/
protected CapacitatedSpanningTreeSolutionRepresentation bestSolution;
/**
* Construct a new abstract capacitated minimum spanning tree algorithm.
*
* @param graph the base graph to calculate the capacitated spanning tree for
* @param root the root of the capacitated spanning tree
* @param capacity the edge capacity constraint
* @param demands the demands of the vertices
*/
protected AbstractCapacitatedMinimumSpanningTree(
Graph graph, V root, double capacity, Map demands)
{
this.graph = Objects.requireNonNull(graph, "Graph cannot be null");
if (!graph.getType().isUndirected()) {
throw new IllegalArgumentException("Graph must be undirected");
}
if (!new ConnectivityInspector<>(graph).isConnected()) {
throw new IllegalArgumentException(
"Graph must be connected. Otherwise, there is no capacitated minimum spanning tree.");
}
this.root = Objects.requireNonNull(root, "Root cannot be null");
this.capacity = capacity;
this.demands = Objects.requireNonNull(demands, "Demands cannot be null");
for (V vertex : graph.vertexSet()) {
if (vertex != root) {
Double demand = demands.get(vertex);
if (demand == null) {
throw new IllegalArgumentException(
"Demands does not provide a demand for every vertex.");
}
if (demand > capacity) {
throw new IllegalArgumentException(
"Demands must not be greater than the capacity. Otherwise, there is no capacitated minimum spanning tree.");
}
}
}
this.bestSolution = new CapacitatedSpanningTreeSolutionRepresentation();
}
@Override
public abstract CapacitatedSpanningTree getCapacitatedSpanningTree();
/**
* This class represents a solution instance by managing the labels and the partition mapping.
* With the help of this class, a capacitated spanning tree based on the label and partition
* mapping can be calculated.
*/
protected class CapacitatedSpanningTreeSolutionRepresentation
implements
Cloneable
{
/**
* 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.
*/
private Map labels;
/**
* the implicit partition defined by the subtrees
*/
private Map, Double>> partition;
/**
* the next free label
*/
private int nextFreeLabel;
/**
* Constructs a new solution representation for the CMST problem.
*/
public CapacitatedSpanningTreeSolutionRepresentation()
{
this(new HashMap<>(), new HashMap<>());
}
/**
* Constructs a new solution representation for the CMST problem based on
* labels
and partition
. All labels have to be positive.
*
* @param labels the labels of the subsets in the partition
* @param partition the partition map
*/
public CapacitatedSpanningTreeSolutionRepresentation(
Map labels, Map, Double>> partition)
{
for (Integer i : labels.values()) {
if (i < 0) {
throw new IllegalArgumentException("Labels are not non-negative");
}
}
for (Integer i : partition.keySet()) {
if (i < 0) {
throw new IllegalArgumentException("Labels are not non-negative");
}
}
this.labels = labels;
this.partition = partition;
getNextFreeLabel();
}
/**
* Calculates the resulting spanning tree based on this solution representation.
*
* @return the resulting spanning tree based on this solution representation
*/
public CapacitatedSpanningTreeAlgorithm.CapacitatedSpanningTree calculateResultingSpanningTree()
{
Set spanningTreeEdges = new HashSet<>();
double weight = 0;
for (Pair, Double> part : partition.values()) {
// get spanning tree on the part inclusive the root vertex
Set set = part.getFirst();
set.add(root);
SpanningTreeAlgorithm.SpanningTree subtree =
new PrimMinimumSpanningTree<>(new AsSubgraph<>(graph, set, graph.edgeSet()))
.getSpanningTree();
set.remove(root);
// add the partial solution to the overall solution
spanningTreeEdges.addAll(subtree.getEdges());
weight += subtree.getWeight();
}
return new CapacitatedSpanningTreeImpl<>(labels, partition, spanningTreeEdges, weight);
}
/**
* Moves vertex
from the subset represented by fromLabel
to the
* subset represented by toLabel
.
*
* @param vertex the vertex to move
* @param fromLabel the subset to move the vertex from
* @param toLabel the subset to move the vertex to
*/
public void moveVertex(V vertex, Integer fromLabel, Integer toLabel)
{
labels.put(vertex, toLabel);
Set oldPart = partition.get(fromLabel).getFirst();
oldPart.remove(vertex);
partition.put(
fromLabel,
Pair.of(oldPart, partition.get(fromLabel).getSecond() - demands.get(vertex)));
if (!partition.keySet().contains(toLabel)) {
partition.put(toLabel, Pair.of(new HashSet<>(), 0.0));
}
Set newPart = partition.get(toLabel).getFirst();
newPart.add(vertex);
partition.put(
toLabel,
Pair.of(newPart, partition.get(toLabel).getSecond() + demands.get(vertex)));
}
/**
* Moves all vertices in vertices
from the subset represented by
* fromLabel
to the subset represented by toLabel
.
*
* @param vertices the vertices to move
* @param fromLabel the subset to move the vertices from
* @param toLabel the subset to move the vertices to
*/
public void moveVertices(Set vertices, Integer fromLabel, Integer toLabel)
{
// update labels and calculate weight change
double weightOfVertices = 0;
for (V v : vertices) {
weightOfVertices += demands.get(v);
labels.put(v, toLabel);
}
// update partition
if (!partition.keySet().contains(toLabel)) {
partition.put(toLabel, Pair.of(new HashSet<>(), 0.0));
}
Set newPart = partition.get(toLabel).getFirst();
newPart.addAll(vertices);
partition.put(
toLabel, Pair.of(newPart, partition.get(toLabel).getSecond() + weightOfVertices));
Set oldPart = partition.get(fromLabel).getFirst();
oldPart.removeAll(vertices);
partition.put(
fromLabel,
Pair.of(oldPart, partition.get(fromLabel).getSecond() - weightOfVertices));
}
/**
* Refines the partition by adding new subsets if the designated root has more than one
* subtree in the subset label
of the partition.
*
* @param vertexSubset the subset represented by label
, that is the subset that
* has to be refined
* @param label the label of the subset of the partition that were refined
*
* @return the set of all labels of subsets that were changed during the refinement
*/
public Set partitionSubtreesOfSubset(Set vertexSubset, int label)
{
List> subtreesOfSubset = new LinkedList<>();
if (vertexSubset.isEmpty()) {
return new HashSet<>();
}
// initialize a subgraph containing the MST of the subset
vertexSubset.add(root);
SpanningTreeAlgorithm.SpanningTree spanningTree = new PrimMinimumSpanningTree<>(
new AsSubgraph<>(graph, vertexSubset, graph.edgeSet())).getSpanningTree();
Graph spanningTreeGraph =
new AsSubgraph<>(graph, vertexSubset, spanningTree.getEdges());
int degreeOfRoot = spanningTreeGraph.degreeOf(root);
if (degreeOfRoot == 1) {
vertexSubset.remove(root);
return new HashSet<>();
}
// store the affected labels
Set affectedLabels = new HashSet<>();
// search for subtrees rooted at root
DepthFirstIterator depthFirstIterator =
new DepthFirstIterator<>(spanningTreeGraph, root);
if (depthFirstIterator.hasNext()) {
depthFirstIterator.next();
}
int numberOfRootEdgesExplored = 0;
Set currentSubtree = new HashSet<>();
while (depthFirstIterator.hasNext()) {
V next = depthFirstIterator.next();
// exploring new subtree
if (spanningTreeGraph.containsEdge(root, next)) {
if (!currentSubtree.isEmpty()) {
subtreesOfSubset.add(currentSubtree);
currentSubtree = new HashSet<>();
}
numberOfRootEdgesExplored++;
// we do not have to move more vertices
if (numberOfRootEdgesExplored == degreeOfRoot) {
break;
}
}
currentSubtree.add(next);
}
// move the subtrees to new subsets in the partition
for (Set subtree : subtreesOfSubset) {
int nextLabel = this.getNextFreeLabel();
this.moveVertices(subtree, label, nextLabel);
affectedLabels.add(nextLabel);
}
vertexSubset.remove(root);
return affectedLabels;
}
/**
* Cleans up the solution representation by removing all empty sets from the partition.
*/
public void cleanUp()
{
partition.entrySet().removeIf(entry -> entry.getValue().getFirst().isEmpty());
}
/**
* Returns the next free label in the label map respectively partition
*
* @return the next free label in the label map respectively partition
*/
public int getNextFreeLabel()
{
int freeLabel = nextFreeLabel;
nextFreeLabel++;
while (partition.keySet().contains(nextFreeLabel)) {
nextFreeLabel++;
}
return freeLabel;
}
/**
* Returns the label of the subset that contains vertex
.
*
* @param vertex the vertex to return the label from
*
* @return the label of vertex
*/
public int getLabel(V vertex)
{
return labels.get(vertex);
}
/**
* Returns all labels of all subsets.
*
* @return the labels of all subsets
*/
public Set getLabels()
{
return partition.keySet();
}
/**
* Returns the set of vertices that are in the subset with label label
.
*
* @param label the label of the subset to return the vertices from
*
* @return the set of vertices that are in the subset with label label
*/
public Set getPartitionSet(Integer label)
{
return partition.get(label).getFirst();
}
/**
* Returns the sum of the weights of all vertices that are in the subset with label
* label
.
*
* @param label the label of the subset to return the weight from
*
* @return the sum of the weights of all vertices that are in the subset with label
* label
*/
public double getPartitionWeight(Integer label)
{
return partition.get(label).getSecond();
}
/**
* Returns a shallow copy of this solution representation instance. Vertices are not cloned.
*
* @return a shallow copy of this solution representation.
*
* @throws RuntimeException in case the clone is not supported
*
* @see java.lang.Object#clone()
*/
public CapacitatedSpanningTreeSolutionRepresentation clone()
{
try {
CapacitatedSpanningTreeSolutionRepresentation capacitatedSpanningTreeSolutionRepresentation =
TypeUtil.uncheckedCast(super.clone());
capacitatedSpanningTreeSolutionRepresentation.labels = new HashMap<>(labels);
capacitatedSpanningTreeSolutionRepresentation.partition = new HashMap<>();
for (Map.Entry, Double>> entry : this.partition.entrySet()) {
capacitatedSpanningTreeSolutionRepresentation.partition.put(
entry.getKey(),
Pair.of(
new HashSet<>(entry.getValue().getFirst()),
entry.getValue().getSecond()));
}
capacitatedSpanningTreeSolutionRepresentation.nextFreeLabel = this.nextFreeLabel;
return capacitatedSpanningTreeSolutionRepresentation;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy