Skip to content

Commit

Permalink
DIMACS IO Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ZurAougav committed Aug 1, 2023
1 parent 252ebab commit d994d70
Show file tree
Hide file tree
Showing 2 changed files with 268 additions and 5 deletions.
155 changes: 150 additions & 5 deletions jgalgo-io/src/main/java/com/jgalgo/io/FormatDIMACS.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
*/
package com.jgalgo.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.util.List;

import com.jgalgo.graph.Graph;
import com.jgalgo.graph.GraphBuilder;
import com.jgalgo.graph.Weights;

class FormatDIMACS implements GraphFormat {

private FormatDIMACS() {}
private FormatDIMACS() {
}

static final FormatDIMACS Instance = new FormatDIMACS();

Expand All @@ -48,18 +54,157 @@ private static class WriterImpl implements GraphWriter {

@Override
public void writeGraph(Graph graph, Writer writer) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'writeGraph'");
try {
writer.append("c DIMACS written graph by JGAlgo") //
.append(System.lineSeparator());
final int num_vertices = graph.vertices().size();
final int num_edges = graph.edges().size();
final Weights.Int w = graph.getEdgesWeights("weightsEdges");
final boolean hash_weights = w != null;
if (hash_weights)
writer.append("p sp " + num_vertices + " " + num_edges) //
.append(System.lineSeparator());
else
writer.append("p edge " + num_vertices + " " + num_edges) //
.append(System.lineSeparator());

// writes all edges, optional with weights
for (int e : graph.edges()) {
writer.append("e " + graph.edgeSource(e) + " " + graph.edgeTarget(e));
if (hash_weights) {
int the_weight = w.getInt(e);
writer.append(" " + the_weight);
}
writer.append(System.lineSeparator());
}

} catch (IOException e) {
throw new UncheckedIOException(e);
}

}

}

private static class ReaderImpl implements GraphReader {

/**
* Support 2 DIMACS formats:<br>
* The "DIMACS edge format" and "DIMACS sp format"<br>
* <br>
* 1. Basic DIMACS format: <br>
* see https://github.com/akinanop/mvl-solver/wiki/DIMACS-Graph-Format
*
* <pre>
p edge <NumVertices> <NumEdges>
e <VertexName1> <VertexName2>
Example:
c this is the graph with vertices {1,2,3,4,5} and edges {(1,2),(2,3),(2,4),(3,4),(4,5)}
p edge 5 5
e 1 2
e 2 3
e 2 4
e 3 4
e 4 5
* </pre>
*
* 2. Shortest path format (with weights)<br>
* Two assumptions:<br>
* (1) Undirected graph.<br>
* (2) Weights are integers.<br>
* <br>
* The .gr files:<br>
* see http://www.diag.uniroma1.it/challenge9/format.shtml#graph
*
* <pre>
c
p sp n nm
a u v w
* </pre>
*/
@Override
public GraphBuilder readIntoBuilder(Reader reader) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'readIntoBuilder'");
try (BufferedReader br = reader instanceof BufferedReader ? (BufferedReader) reader
: new BufferedReader(reader)) {
GraphBuilder gb = null;
Weights.Int w = null;
String line;
String graph_format = null;
int num_vertices = -1;
int num_edges = -1;
while ((line = br.readLine()) != null) {
// skip empty and comment lines (starts with "c")
if (line.trim().length() == 0 || line.trim().startsWith("c"))
continue;
// replace multiple spaces with just one space
line = line.trim().replaceAll("\\s+", " ");
if (line.trim().startsWith("p")) {
String[] arr = line.split(" ");
if (arr.length < 4)
throw new IllegalArgumentException(
"p lines must have 4 parameters: p edge <NumVertices> <NumEdges> or p sp <NumVertices> <NumEdges>");
graph_format = arr[1].toLowerCase();
if (!(graph_format.equals("sp") || graph_format.equals("edge")))
throw new IllegalArgumentException(
"support only: p edge <NumVertices> <NumEdges> or p sp <NumVertices> <NumEdges>");
try {
num_vertices = Integer.parseInt(arr[2]);
num_edges = Integer.parseInt(arr[3]);
} catch (Exception e) {
throw new IllegalArgumentException(
"expect numbers: p edge <NumVertices> <NumEdges> or p sp <NumVertices> <NumEdges>");
}
if (num_vertices < 1)
return gb;
gb = GraphBuilder.newUndirected();
if (graph_format.equals("sp"))
w = gb.addEdgesWeights("weightsEdges", int.class);
for (int i = 0; i < num_vertices; i++)
gb.addVertex(i + 1); // vertices are labeled as 1,2,3,4...
if (num_edges < 1)
return gb;
continue;
}

// here, we expect edge definition
// e source_vertice destination_vertice [weight]
if (!line.trim().toLowerCase().startsWith("e"))
throw new IllegalArgumentException(
"expect edge definition: e <source_vertice> <destination_vertice>");
String[] arr = line.split(" ");
if (graph_format.equals("sp") && arr.length < 4)
throw new IllegalArgumentException(
"expect edge definition: e <source_vertice> <destination_vertice> <weight>");
if (graph_format.equals("edge") && arr.length < 3)
throw new IllegalArgumentException(
"expect edge definition: e <source_vertice> <destination_vertice>");
int vertice_source = -1;
int vertice_target = -1;
int edge_weight = -1;
try {
vertice_source = Integer.parseInt(arr[1]);
vertice_target = Integer.parseInt(arr[2]);
if (graph_format.equals("sp"))
edge_weight = Integer.parseInt(arr[3]);
} catch (Exception e) {
if (graph_format.equals("sp"))
throw new IllegalArgumentException("edge must have 2 vertices as numbers and a weight");
throw new IllegalArgumentException("edge must have 2 vertices as numbers");
}
if (vertice_source < 1 || vertice_source > num_vertices //
|| vertice_target < 1 || vertice_target > num_vertices)
throw new IllegalArgumentException("vertice nmber must be betwen 1 and num_vertices");
// vertices labels 1..num_vertices are mapped to 0..num_vertices-1
final int e = gb.addEdge(vertice_source, vertice_target);
if (graph_format.equals("sp"))
w.set(e, edge_weight);
continue;
}
return gb;

} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

}
Expand Down
118 changes: 118 additions & 0 deletions jgalgo-io/src/test/java/com/jgalgo/io/FormatDIMACSTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*-
* Copyright 2023 Barak Ugav
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.jgalgo.io;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Random;
import org.junit.jupiter.api.Test;
import com.jgalgo.graph.Graph;
import com.jgalgo.graph.GraphBuilder;
import com.jgalgo.graph.GraphFactory;
import com.jgalgo.graph.Weights;

public class FormatDIMACSTest {

@Test
public void parseDimacsEdgeGraph() {
String data = "";
data += "c this is the graph with vertices {1,2,3,4,5} and edges {(1,2),(2,3),(2,4),(3,4),(4,5)}\n";
data += "p edge 5 5\n";
data += "e 1 2\n";
data += "e 2 3\n";
data += "e 2 4\n";
data += "e 3 4\n";
data += "e 4 5\n";
final Graph g = GraphReader.newInstance("dimacs").readGraph(new StringReader(data));

final StringWriter writer = new StringWriter();
GraphWriter.newInstance("dimacs").writeGraph(g, writer);
String data2 = writer.toString();

String check_data2 = "";
check_data2 += "c DIMACS written graph by JGAlgo\n";
check_data2 += "p edge 5 5\n";
check_data2 += "e 1 2\n";
check_data2 += "e 2 3\n";
check_data2 += "e 2 4\n";
check_data2 += "e 3 4\n";
check_data2 += "e 4 5\n";
check_data2 = check_data2.replace("\n", System.lineSeparator());
String check_data3 = "";
check_data3 += "c DIMACS written graph by JGAlgo\n";
check_data3 += "p edge 5 5\n";
check_data3 += "e 1 2\n";
check_data3 += "e 2 4\n";
check_data3 += "e 2 3\n";
check_data3 += "e 3 4\n";
check_data3 += "e 4 5\n";
check_data3 = check_data3.replace("\n", System.lineSeparator());

if (data2.trim().equals(check_data2.trim()))
assertEquals(data2.trim(), check_data2.trim());
else if (data2.trim().equals(check_data3.trim()))
assertEquals(data2.trim(), check_data3.trim());
else
assertEquals(data2.trim(), check_data2.trim());
}

@Test
public void parseDimacsSpGraph() {
String data = "";
data += "c this is the graph\n";
data += "p sp 5 5\n";
data += "e 1 2 3\n";
data += "e 2 3 9\n";
data += "e 2 4 15\n";
data += "e 3 4 2\n";
data += "e 4 5 7\n";
final Graph g = GraphReader.newInstance("dimacs").readGraph(new StringReader(data));

final StringWriter writer = new StringWriter();
GraphWriter.newInstance("dimacs").writeGraph(g, writer);
String data2 = writer.toString();

String check_data2 = "";
check_data2 += "c DIMACS written graph by JGAlgo\n";
check_data2 += "p sp 5 5\n";
check_data2 += "e 1 2 3\n";
check_data2 += "e 2 3 9\n";
check_data2 += "e 2 4 15\n";
check_data2 += "e 3 4 2\n";
check_data2 += "e 4 5 7\n";
check_data2 = check_data2.replace("\n", System.lineSeparator());
String check_data3 = "";
check_data3 += "c DIMACS written graph by JGAlgo\n";
check_data3 += "p sp 5 5\n";
check_data3 += "e 1 2 3\n";
check_data3 += "e 2 4 15\n";
check_data3 += "e 2 3 9\n";
check_data3 += "e 3 4 2\n";
check_data3 += "e 4 5 7\n";
check_data3 = check_data3.replace("\n", System.lineSeparator());

if (data2.trim().equals(check_data2.trim()))
assertEquals(data2.trim(), check_data2.trim());
else if (data2.trim().equals(check_data3.trim()))
assertEquals(data2.trim(), check_data3.trim());
else
assertEquals(data2.trim(), check_data2.trim());
}

}

0 comments on commit d994d70

Please sign in to comment.