Skip to content

Commit

Permalink
ConstraintValidator
Browse files Browse the repository at this point in the history
Final steps towards implementing the ConstraintValidator.
  • Loading branch information
TiloW committed Sep 13, 2014
1 parent 0964f02 commit a5de052
Show file tree
Hide file tree
Showing 18 changed files with 1,085 additions and 98 deletions.
22 changes: 12 additions & 10 deletions src/main/java/proof/data/Graph.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
*/
public class Graph {

private int[][] edgeIndices;
private double[] costs;
private final int[][] edgeIndices;
private final double[] costs;
private boolean immutable;

public final static int NO_EDGE = -1;
Expand Down Expand Up @@ -54,14 +54,8 @@ public Graph(int numberOfNodes, int numberOfEdges) {
* @return {@code true} iff the edge exist
*/
public boolean edgeExists(int source, int target) {
if (source < 0 || source >= edgeIndices.length) {
throw new IllegalArgumentException("source node out of range: " + source);
}
if (target < 0 || target >= edgeIndices.length) {
throw new IllegalArgumentException("target node out of range: " + source);
}

return edgeIndices[source][target] != NO_EDGE;
return source >= 0 && source < edgeIndices.length && target >= 0 && target < edgeIndices.length
&& edgeIndices[source][target] != NO_EDGE;
}

/**
Expand Down Expand Up @@ -115,6 +109,14 @@ public void addEdge(int edgeId, int source, int target, double cost) {
throw new IllegalArgumentException("Edge index out of bounds: " + edgeId);
}

if (source < 0 || source >= edgeIndices.length) {
throw new IllegalArgumentException("Source index out of bounds: " + source);
}

if (target < 0 || target >= edgeIndices.length) {
throw new IllegalArgumentException("Target index out of bounds: " + target);
}

if (costs[edgeId] != NO_EDGE_COST) {
throw new IllegalArgumentException("Edge ID already exists!");
}
Expand Down
199 changes: 177 additions & 22 deletions src/main/java/proof/validator/ConstraintValidator.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package proof.validator;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONObject;

import proof.data.CrossingIndex;
import proof.data.Graph;
import proof.data.reader.CrossingReader;
import proof.exception.InvalidConstraintException;
import proof.exception.InvalidPathException;
import proof.validator.base.ObjectValidator;

/**
Expand All @@ -26,68 +30,219 @@ public class ConstraintValidator implements ObjectValidator {
private final static CrossingReader CROSSING_READER = new CrossingReader();

private final Map<CrossingIndex, Boolean> fixedVariables;
private final int numberOfSegments;
private final Graph graph;

public ConstraintValidator(Map<CrossingIndex, Boolean> fixedVariables) {
/**
* Creates a new constraint validator.
*
* @param fixedVariables The branching variables
* @param numberOfSegments The maximum number of segments per edge (i.e. the optimal solution)
* @param graph The original graph we are working on (without any crossings)
*/
public ConstraintValidator(Map<CrossingIndex, Boolean> fixedVariables, int numberOfSegments,
Graph graph) {
this.fixedVariables = fixedVariables;
this.numberOfSegments = numberOfSegments;
this.graph = graph;
}

@Override
public void validate(JSONObject object) throws InvalidConstraintException {
JSONArray crossings = object.getJSONArray("requiredCrossings");

final Map<CrossingIndex, Boolean> vars = new HashMap<CrossingIndex, Boolean>(fixedVariables);
final Set<CrossingIndex> vars = new HashSet<CrossingIndex>();
for (CrossingIndex cross : fixedVariables.keySet()) {
if (fixedVariables.get(cross)) {
vars.add(cross);
}
}

// realize required variables
// override branching variables if required (should never happen but still)
JSONArray crossings = object.getJSONArray("requiredCrossings");

for (int i = 0; i < crossings.length(); i++) {
vars.put(CROSSING_READER.read(crossings.getJSONArray(i)), true);
vars.add(CROSSING_READER.read(crossings.getJSONArray(i)));
}

JSONArray paths = object.getJSONArray("paths");

validatePaths(vars, paths);
PathValidator validator = createPathValidator(vars, numberOfSegments, graph);
for (int i = 0; i < paths.length(); i++) {
try {
validator.validate(paths.getJSONArray(i));
} catch (InvalidPathException e) {
throw (InvalidConstraintException) new InvalidConstraintException(
"Invalid path encountered").initCause(e);
}
}

if (paths.length() == 9) {
validateK33(vars, paths);
validateK33(paths);
} else if (paths.length() == 10) {
validateK5(vars, paths);
validateK5(paths);
} else {
throw new InvalidConstraintException(
"Invalid number of Kuratowski-Paths for single Constraint: " + paths.length());
"Invalid number of Kuratowski paths for single Constraint: " + paths.length());
}
}

protected PathValidator createPathValidator(Set<CrossingIndex> vars, int numberOfSegments,
Graph graph) {
return new PathValidator(vars, numberOfSegments, graph);
}

/**
* For each path, we have to validate the required crossings are realized.
* Asserts that all five nodes are connected to one another.
*
* @param vars
* @param paths
* @param paths The ten paths
* @throws InvalidConstraintException If one of the paths has no defined source or target
*/
private void validatePaths(Map<CrossingIndex, Boolean> vars, JSONArray paths) {
private void validateK5(JSONArray paths) throws InvalidConstraintException {
Map<Integer, Integer> nodes = collectNodes(paths);

if (nodes.size() != 5) {
throw new InvalidConstraintException("Supposed K5 has an invalid number of nodes: "
+ nodes.size());
}

// find all paths
boolean[][] foundPaths = new boolean[5][5];
for (int i = 0; i < 5; i++) {
for (int ii = 0; ii < 5; ii++)
foundPaths[i][ii] = false;
}

for (int i = 0; i < paths.length(); i++) {
JSONArray path = paths.getJSONArray(i);
int u = nodes.get(getSource(path));
int v = nodes.get(getTarget(path));

foundPaths[u][v] = foundPaths[v][u] = true;
}

// assert each required path is present
for (int i = 0; i < 5; i++) {
for (int ii = 0; ii < 5; ii++) {
if (i == ii) {
if (foundPaths[i][ii]) {
throw new InvalidConstraintException("Self loop in supposed Kuratowski path");
}
} else {
if (!foundPaths[i][ii]) {
throw new InvalidConstraintException("Missing Kuratowski path in supposed K5");
}
}
}
}
}

/**
* Asserts that all five nodes are connected to one another.
* Asserts a valid K33, i.e. a bipartite Graph with three nodes in each of the two subsets.
*
* @param vars
* @param paths
* @param paths The 9 paths
* @throws InvalidConstraintException If one of the paths has no defined source or target
*/
private void validateK5(Map<CrossingIndex, Boolean> vars, JSONArray paths) {
// TODO
private void validateK33(JSONArray paths) throws InvalidConstraintException {
Map<Integer, Integer> nodes = collectNodes(paths);

if (nodes.size() != 6) {
throw new InvalidConstraintException("Supposed K33 has an invalid number of nodes: "
+ nodes.size());
}

// classify nodes (2-coloring)
boolean[] color = new boolean[6];
for (int i = 0; i < 6; i++) {
color[i] = false;
}

for (int i = 0; i < paths.length(); i++) {
JSONArray path = paths.getJSONArray(i);
int u = nodes.get(getSource(path));
int v = nodes.get(getTarget(path));

if (u == 0) {
color[v] = true;
} else if (v == 0) {
color[u] = true;
}
}

// validate coloring
int counter = 0;
for (int i = 0; i < 6; i++) {
if (color[i]) {
counter++;
}
}

if (counter != 3) {
throw new InvalidConstraintException("Supposed K33 has invalid 2-coloring");
}

// validate each node is connected to exactly three nodes (of different color)
for (int node = 0; node < 6; node++) {
int edgeCounter = 0;

for (int i = 0; i < paths.length(); i++) {
JSONArray path = paths.getJSONArray(i);
int v = nodes.get(getSource(path));
int w = nodes.get(getTarget(path));

if (color[v] == color[w]) {
throw new InvalidConstraintException(
"Connected nodes in supposed K33 have the same color");
}

if (node == v || node == w) {
edgeCounter++;
}
}

if (edgeCounter != 3) {
throw new InvalidConstraintException(
"Node of supposed K33 is not connected to the right number of nodes");
}
}
}

/**
* Asserts a valid K33, i.e. a bipartit Graph with 3 nodes in each of the two subsets.
* Collects all the nodes present as source or target nodes in the paths. Will enumerate those
* nodes starting at zero. Returns a mapping of original node indices to their normalized indices.
*
* @param vars
* @param paths
* @param paths The paths to be searched for source and target nodes
* @return The resulting node mapping
*/
private Map<Integer, Integer> collectNodes(JSONArray paths) {
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
int counter = 0;

for (int i = 0; i < paths.length(); i++) {
int nodeIndex = getSource(paths.getJSONArray(i));
if (!result.containsKey(nodeIndex)) {
result.put(nodeIndex, counter++);
}
nodeIndex = getTarget(paths.getJSONArray(i));
if (!result.containsKey(nodeIndex)) {
result.put(nodeIndex, counter++);
}
}

return result;
}

/**
* Returns the first node of a valid path.
*/
private int getSource(JSONArray path) {
return path.getJSONObject(0).getInt("from");
}

/**
* Returns the last node of a valid path.
*/
private void validateK33(Map<CrossingIndex, Boolean> vars, JSONArray paths) {
// TODO
private int getTarget(JSONArray path) {
return path.getJSONObject(path.length() - 1).getInt("to");
}
}
29 changes: 5 additions & 24 deletions src/test/java/proof/data/GraphTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

/**
* Tests for {@link Graph}
*
*
* @author Tilo Wiedera
*
*/
Expand Down Expand Up @@ -105,28 +105,9 @@ public void testEdgeExists_bounds() {
graph.addEdge(0, 0, 1, 10);
graph.addEdge(1, 1, 2, 100);

try {
graph.edgeExists(4, 0);
fail("Invalid source index should fail");
} catch (IllegalArgumentException expected) {
}

try {
graph.edgeExists(-1, 0);
fail("Invalid source index should fail");
} catch (IllegalArgumentException expected) {
}

try {
graph.edgeExists(0, 42);
fail("Invalid target index should fail");
} catch (IllegalArgumentException expected) {
}

try {
graph.edgeExists(0, -666);
fail("Invalid target index should fail");
} catch (IllegalArgumentException expected) {
}
assertFalse(graph.edgeExists(4, 0));
assertFalse(graph.edgeExists(-1, 0));
assertFalse(graph.edgeExists(0, 42));
assertFalse(graph.edgeExists(0, -666));
}
}
Loading

0 comments on commit a5de052

Please sign in to comment.