Skip to content
Snippets Groups Projects
Commit b3329354 authored by Enna Gerhard's avatar Enna Gerhard :levitate:
Browse files

Merge branch 'mce' into 'main'

Maximum clique ennumeration

See merge request !30
parents 5b771391 83e398b8
No related branches found
No related tags found
1 merge request!30Maximum clique ennumeration
Pipeline #206053 passed
package de.uni.bremen.grapa.library.algorithms;
import de.uni.bremen.grapa.library.graphs.api.DirectedEdge;
import de.uni.bremen.grapa.library.graphs.api.DirectedGraph;
import de.uni.bremen.grapa.library.graphs.api.Vertex;
import de.uni.bremen.grapa.library.helper.ConsoleHelper;
import de.uni.bremen.grapa.library.helper.GetUndirectedGraph;
import de.uni.bremen.grapa.library.helper.GraphRenaming;
import de.uni.bremen.grapa.library.helper.GraphRenamingResult;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
/**
* Calls a native MCE solver on an undirected graph instance.
* The solver is expected to be an executable binary called mce_solver in the working directory.
*/
public class NativeMaximumCliqueEnnumerationSolver {
private GetUndirectedGraph getUndirectedGraph = new GetUndirectedGraph();
/**
* Solves the vertex cover problem on a graph.
* Requires the directed graph to be in fact undirected.
*
* @param graph an undirected directed graph
* @return a vertex cover solution
*/
public Collection<Set<Vertex>> solveMaximumCliqueEnumeration(final DirectedGraph<Vertex> graph) {
final DirectedGraph<Vertex> blueGraph = getUndirectedGraph.getBlueGraph(graph);
File solver = new File("mce_solver");
if (!solver.exists()) {
// Use packaged mce_solver instead
try (InputStream solverResource = (getClass().getResourceAsStream("/mce_solver"))) {
solver = File.createTempFile("mce_solver", "");
assert solverResource != null;
Files.copy(solverResource, solver.toPath(), StandardCopyOption.REPLACE_EXISTING);
// Set executable flag on solver.
// Return value may be unused, cause failure to set this will result in appropriate error later
solver.setExecutable(true);
// Clear the temporary file upon JVM shutdown
solver.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
final String[] command = {solver.getAbsolutePath(), "--input-file=ignored", "--algorithm=adjlist"};
final ProcessBuilder processBuilder = new ProcessBuilder(command);
final GraphRenamingResult rename = new GraphRenaming().rename(blueGraph);
final DirectedGraph<Vertex> renamedGraph = rename.renamedGraph();
try {
final Process process = processBuilder.start();
final int offset = renamedGraph.vertexExists(0) ? 1 : 0;
final Scanner scanner = new Scanner(new BufferedInputStream(process.getInputStream()));
writeProblem(process, renamedGraph, offset);
final List<Set<Vertex>> readResult = readResult(renamedGraph, offset, scanner);
final Scanner errors = new Scanner(process.getErrorStream());
while (errors.hasNext()) {
ConsoleHelper.println("[MCE] " + errors.nextLine());
}
return rename.decipher(readResult);
} catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException("MCE Solver not successful");
}
}
/**
* Reads a result from the mce solver.
*
* @param graph input graph
* @param offset an offset which needs to be substracted as the input was shifted to avoid exceptions
* @return an ennumeration of maximum cliques determined by the solver
*/
private List<Set<Vertex>> readResult(final DirectedGraph<Vertex> graph, final int offset, final Scanner scanner) {
List<Set<Vertex>> result = new ArrayList<>();
while (scanner.hasNext()) {
final String line = scanner.nextLine();
if (line.startsWith("NOTE") || line.startsWith("Reading")) {
continue;
}
final String[] split = line.split(" ");
if (split.length <= 1) {
continue;
}
Set<Vertex> newClique = new HashSet<>();
for (final String entry : split) {
newClique.add(graph.getVertex(Integer.parseInt(entry) - offset));
}
result.add(newClique);
}
return result;
}
/**
* Writes a problem for the mce solver in its own process.
*
* @param process process called
* @param graph graph to write
* @param offset can be passed to eliminate values smaller than 1, needs to be kept in mind when parsin the result
*/
private void writeProblem(final Process process, final DirectedGraph<Vertex> graph, final int offset) {
final PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(
process.getOutputStream())));
writer.println(graph.maxId() + 1);
writer.println(graph.edgeCount() / 2);
for (final DirectedEdge edge : graph.getEdges()) {
if (edge.getStartId() < edge.getEndId()) {
writer.println((edge.getStartId() + offset) + "," + (edge.getEndId() + offset));
}
}
writer.close();
}
}
......@@ -70,12 +70,12 @@ public class NativeVcSolver {
}
/**
* Reads a result from the peaty solver.
* Reads a result from the vc solver.
*
* @param graph input graph
* @param process process running peaty
* @param process process running solver
* @param offset an offset which needs to be substracted as the input was shifted to avoid exceptions
* @return a minimum vertex cover determined by peaty
* @return a minimum vertex cover determined by the solver
*/
private Set<Vertex> readResult(final DirectedGraph<Vertex> graph, final Process process, final int offset) {
Set<Vertex> result = new HashSet<>();
......@@ -95,7 +95,7 @@ public class NativeVcSolver {
}
/**
* Writes a problem for the peaty solver in its own process.
* Writes a problem for the vc solver in its own process.
*
* @param process process called
* @param graph graph to write
......
package de.uni.bremen.grapa.library.helper;
import de.uni.bremen.grapa.library.graphs.api.DirectedEdge;
import de.uni.bremen.grapa.library.graphs.api.DirectedGraph;
import de.uni.bremen.grapa.library.graphs.api.Vertex;
import java.util.ArrayList;
/**
* Easy access to the undirected graph.
*/
public class GetUndirectedGraph {
/**
* Strips away red edges.
*
* @param originalGraph original graph
* @return just the blue graph with undirected edges
*/
public DirectedGraph<Vertex> getBlueGraph(DirectedGraph<Vertex> originalGraph) {
final DirectedGraph<Vertex> blueGraph = originalGraph.shadowCopy();
for (final DirectedEdge edge : new ArrayList<>(blueGraph.getEdges())) {
if (blueGraph.edgeExists(edge.reverse())) {
continue;
} else {
blueGraph.removeEdge(edge);
}
}
for (final Vertex vertex : new ArrayList<>(blueGraph.getVertices())) {
if (blueGraph.inDegreeOf(vertex) == 0) {
blueGraph.removeVertex(vertex);
}
}
return blueGraph;
}
}
package de.uni.bremen.grapa.library.helper;
import de.uni.bremen.grapa.library.graphs.api.DirectedGraph;
import de.uni.bremen.grapa.library.graphs.api.Edge;
import de.uni.bremen.grapa.library.graphs.api.Vertex;
import de.uni.bremen.grapa.library.graphs.implementation.directed.BasicDirectedGraph;
import java.util.HashMap;
import java.util.Map;
/**
* Renames graphs to only make use of the lowest vertex ids.
*/
public class GraphRenaming {
/**
* Renames a graph.
*
* @param graph graph to be renamed
* @return result of renaming and a mapping to the original ids
*/
public GraphRenamingResult rename(DirectedGraph<Vertex> graph) {
int[] backMapping = graph.getVertices().stream().mapToInt(Vertex::getId).toArray();
Map<Integer, Integer> forwardMapping = new HashMap<>();
DirectedGraph<Vertex> replacement = new BasicDirectedGraph();
for (int i = 0; i < backMapping.length; i++) {
forwardMapping.put(backMapping[i], i);
replacement.addVertex(new Vertex(i));
}
for (final Edge edge : graph.getEdges()) {
replacement.addEdge(forwardMapping.get(edge.getStartId()), forwardMapping.get(edge.getEndId()));
}
return new GraphRenamingResult(backMapping, replacement);
}
}
package de.uni.bremen.grapa.library.helper;
import de.uni.bremen.grapa.library.graphs.api.DirectedGraph;
import de.uni.bremen.grapa.library.graphs.api.Vertex;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A mapping and a renamed graph.
*
* @param mapping the previous value at the respective index
* @param renamedGraph the directed graph
*/
public record GraphRenamingResult(int[] mapping, DirectedGraph<Vertex> renamedGraph) {
/**
* Interprets vertices in the translated representation and converts them back to the original.
*
* @param input in the renamed space
* @return in the original space
*/
public Collection<Set<Vertex>> decipher(final Collection<Set<Vertex>> input) {
return input.stream().map(this::decipher).toList();
}
/**
* Interprets vertices in the translated representation and converts them back to the original.
*
* @param input in the renamed space
* @return in the original space
*/
public Set<Vertex> decipher(final Set<Vertex> input) {
return input.stream()
.mapToInt(Vertex::getId)
.map(id -> mapping[id])
.mapToObj(Vertex::new)
.collect(Collectors.toSet());
}
}
This diff is collapsed.
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment