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

org.jgrapht.alg.vertexcover.GreedyVCImpl Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2016-2023, by Joris Kinable 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.vertexcover;

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

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

/**
 * Greedy algorithm to find a vertex cover for a graph. A vertex cover is a set of vertices that
 * touches all the edges in the graph. The graph's vertex set is a trivial cover. However, a
 * minimal vertex set (or at least an approximation for it) is usually desired. Finding a
 * true minimal vertex cover is an NP-Complete problem. For more on the vertex cover problem, see
 * 
 * http://mathworld.wolfram.com/VertexCover.html
 *
 * Note: this class supports pseudo-graphs Runtime: $O(|E| \log |V|)$ This class produces often, but
 * not always, better solutions than the 2-approximation algorithms. Nevertheless, there are
 * instances where the solution is significantly worse. In those cases, consider using
 * {@link ClarksonTwoApproxVCImpl}.
 *
 *
 * @param  the graph vertex type
 * @param  the graph edge type
 *
 * @author Joris Kinable
 */
public class GreedyVCImpl
    implements VertexCoverAlgorithm
{

    private static int vertexCounter = 0;

    private final Graph graph;
    private final Map vertexWeightMap;

    /**
     * Constructs a new GreedyVCImpl instance where all vertices have uniform weights.
     * 
     * @param graph input graph
     */
    public GreedyVCImpl(Graph graph)
    {
        this.graph = GraphTests.requireUndirected(graph);
        this.vertexWeightMap = graph
            .vertexSet().stream().collect(Collectors.toMap(Function.identity(), vertex -> 1.0));
    }

    /**
     * Constructs a new GreedyVCImpl instance
     * 
     * @param graph input graph
     * @param vertexWeightMap mapping of vertex weights
     */
    public GreedyVCImpl(Graph graph, Map vertexWeightMap)
    {
        this.graph = GraphTests.requireUndirected(graph);
        this.vertexWeightMap = Objects.requireNonNull(vertexWeightMap);
    }

    /**
     * Finds a greedy solution to the minimum weighted vertex cover problem. At each iteration, the
     * algorithm picks the vertex v with the smallest ratio {@code weight(v)/degree(v)} and adds it
     * to the cover. Next vertex v and all edges incident to it are removed. The process repeats
     * until all vertices are covered. Runtime: O(|E|*log|V|)
     *
     * @return greedy solution
     */
    @Override
    public VertexCoverAlgorithm.VertexCover getVertexCover()
    {
        Set cover = new LinkedHashSet<>();
        double weight = 0;

        // Create working graph: for every vertex, create a RatioVertex which maintains its own list
        // of neighbors
        Map> vertexEncapsulationMap = new HashMap<>();
        graph.vertexSet().stream().filter(v -> graph.degreeOf(v) > 0).forEach(
            v -> vertexEncapsulationMap
                .put(v, new RatioVertex<>(vertexCounter++, v, vertexWeightMap.get(v))));

        for (E e : graph.edgeSet()) {
            V u = graph.getEdgeSource(e);
            RatioVertex ux = vertexEncapsulationMap.get(u);
            V v = graph.getEdgeTarget(e);
            RatioVertex vx = vertexEncapsulationMap.get(v);
            ux.addNeighbor(vx);
            vx.addNeighbor(ux);

            assert (ux.neighbors.get(vx).intValue() == vx.neighbors
                .get(ux)
                .intValue()) : " in an undirected graph, if vx is a neighbor of ux, then ux must be a neighbor of vx";
        }

        TreeSet> workingGraph = new TreeSet<>();
        workingGraph.addAll(vertexEncapsulationMap.values());
        assert (workingGraph.size() == vertexEncapsulationMap
            .size()) : "vertices in vertexEncapsulationMap: " + graph.vertexSet().size()
                + "vertices in working graph: " + workingGraph.size();

        while (!workingGraph.isEmpty()) { // Continue until all edges are covered

            // Find a vertex vx for which W(vx)/degree(vx) is minimal
            RatioVertex vx = workingGraph.pollFirst();
            assert (workingGraph.parallelStream().allMatch(
                ux -> vx.getRatio() <= ux
                    .getRatio())) : "vx does not have the smallest ratio among all elements. VX: "
                        + vx + " WorkingGraph: " + workingGraph;

            for (RatioVertex nx : vx.neighbors.keySet()) {

                if (nx == vx) // Ignore self loops
                    continue;

                workingGraph.remove(nx);

                // Delete vx from nx' neighbor list. Delete nx from the graph and place it back,
                // thereby updating the ordering of the graph
                nx.removeNeighbor(vx);

                if (nx.getDegree() > 0)
                    workingGraph.add(nx);

            }

            // Update cover
            cover.add(vx.v);
            weight += vertexWeightMap.get(vx.v);
            assert (workingGraph.parallelStream().noneMatch(
                ux -> ux.id == vx.id)) : "vx should no longer exist in the working graph";
        }
        return new VertexCoverAlgorithm.VertexCoverImpl<>(cover, weight);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy