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

org.jgrapht.alg.cycle.AbstractFundamentalCycleBasis Maven / Gradle / Ivy

/*
 * (C) Copyright 2016-2021, by Dimitrios Michail 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.cycle;

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

import java.util.*;
import java.util.stream.*;

/**
 * A base implementation for the computation of a fundamental cycle basis of a graph. Subclasses
 * should only provide a method for constructing a spanning forest of the graph. A cycle basis is
 * fundamental if and only if each cycle in the basis contains at least one edge which is not
 * contained in any other cycle in the basis.
 * 
 * 

* For information on algorithms and heuristics for the computation of fundamental cycle bases see * the following paper: Narsingh Deo, G. Prabhu, and M. S. Krishnamoorthy. Algorithms for Generating * Fundamental Cycles in a Graph. ACM Trans. Math. Softw. 8, 1, 26-42, 1982. * *

* The implementation returns a fundamental cycle basis as an undirected cycle basis. For a * discussion of different kinds of cycle bases in graphs see the following paper: Christian * Liebchen, and Romeo Rizzi. Classes of Cycle Bases. Discrete Applied Mathematics, 155(3), 337-355, * 2007. * * @param the vertex type * @param the edge type * * @author Dimitrios Michail */ public abstract class AbstractFundamentalCycleBasis implements CycleBasisAlgorithm { protected Graph graph; /** * Constructor * * @param graph the input graph */ public AbstractFundamentalCycleBasis(Graph graph) { this.graph = GraphTests.requireDirectedOrUndirected(graph); } /** * {@inheritDoc} */ @Override public CycleBasis getCycleBasis() { // compute spanning forest Map spanningForest = computeSpanningForest(); // collect set with all tree edges Set treeEdges = spanningForest .entrySet().stream().map(Map.Entry::getValue).filter(Objects::nonNull) .collect(Collectors.toSet()); // build cycles for all non-tree edges Set> cycles = new LinkedHashSet<>(); int length = 0; double weight = 0d; for (E e : graph.edgeSet()) { if (!treeEdges.contains(e)) { Pair, Double> c = buildFundamentalCycle(e, spanningForest); cycles.add(c.getFirst()); length += c.getFirst().size(); weight += c.getSecond(); } } // return result return new CycleBasisImpl<>(graph, cycles, length, weight); } /** * Compute a spanning forest of the graph. * *

* The representation assumes that the map contains the predecessor edge of each vertex in the * forest. The predecessor edge is the forest edge that was used to discover the vertex. If no * such edge was used (the vertex is a leaf in the forest) then the corresponding entry must be * null. * * @return a map representation of a spanning forest. */ protected abstract Map computeSpanningForest(); /** * Given a non-tree edge and a spanning tree (forest) build a fundamental cycle. * * @param e a non-tree (forest) edge * @param spanningForest the spanning tree (forest) * @return a fundamental cycle */ private Pair, Double> buildFundamentalCycle(E e, Map spanningForest) { V source = graph.getEdgeSource(e); V target = graph.getEdgeTarget(e); // handle self-loops if (source.equals(target)) { return Pair.of(Collections.singletonList(e), graph.getEdgeWeight(e)); } /* * traverse half cycle */ Set path1 = new LinkedHashSet<>(); path1.add(e); V cur = source; while (!cur.equals(target)) { E edgeToParent = spanningForest.get(cur); if (edgeToParent == null) { break; } V parent = Graphs.getOppositeVertex(graph, edgeToParent, cur); path1.add(edgeToParent); cur = parent; } /* * traverse the other half cycle, while removing common edges */ double path2Weight = 0d; LinkedList path2 = new LinkedList<>(); if (!cur.equals(target)) { cur = target; while (true) { E edgeToParent = spanningForest.get(cur); if (edgeToParent == null) { break; } V parent = Graphs.getOppositeVertex(graph, edgeToParent, cur); if (path1.contains(edgeToParent)) { path1.remove(edgeToParent); } else { path2.add(edgeToParent); path2Weight += graph.getEdgeWeight(edgeToParent); } cur = parent; } } // now build cycle for (E a : path1) { path2Weight += graph.getEdgeWeight(a); path2.addFirst(a); } return Pair.of(path2, path2Weight); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy