From df0ed26a595ccffb0323425f61155de77f489f73 Mon Sep 17 00:00:00 2001 From: Nikolai Date: Mon, 29 Nov 2021 17:55:38 +0300 Subject: [PATCH] Fix floating rounding and parallel edges issues in gmwcs --- pom.xml | 2 +- src/main/java/ru/itmo/ctlab/virgo/Main.java | 7 +--- .../ru/itmo/ctlab/virgo/gmwcs/graph/Elem.java | 2 +- .../ctlab/virgo/sgmwcs/solver/Dijkstra.java | 2 ++ .../gmwcs/solver/preprocessing/Dijkstra.kt | 34 +++++++++++-------- .../solver/preprocessing/Preprocessor.kt | 15 +++++--- .../itmo/ctlab/virgo/sgmwcs/SGMWCSTest.java | 28 ++++++++------- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/pom.xml b/pom.xml index 6f3182a..cc6aa32 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ ifmo.ru virgo-solver - 0.1.4 + 0.1.5 UTF-8 diff --git a/src/main/java/ru/itmo/ctlab/virgo/Main.java b/src/main/java/ru/itmo/ctlab/virgo/Main.java index d26aa01..b5a9221 100644 --- a/src/main/java/ru/itmo/ctlab/virgo/Main.java +++ b/src/main/java/ru/itmo/ctlab/virgo/Main.java @@ -1,6 +1,5 @@ package ru.itmo.ctlab.virgo; -import ilog.cplex.IloCplex; import joptsimple.OptionParser; import joptsimple.OptionSet; import ru.itmo.ctlab.gmwcs.solver.TreeSolverKt; @@ -16,12 +15,9 @@ import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Paths; import java.text.ParseException; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -32,7 +28,7 @@ import static ru.itmo.ctlab.virgo.gmwcs.graph.Elem.extract; public class Main { - public static final String VERSION = "0.1.4"; + public static final String VERSION = "0.1.5"; private static void checkCplex() { PrintStream stdout = System.out; @@ -51,7 +47,6 @@ private static void checkCplex() { System.exit(1); } finally { System.setOut(stdout); - System.out.println("aaa"); } } diff --git a/src/main/java/ru/itmo/ctlab/virgo/gmwcs/graph/Elem.java b/src/main/java/ru/itmo/ctlab/virgo/gmwcs/graph/Elem.java index 50163aa..dbe71a6 100644 --- a/src/main/java/ru/itmo/ctlab/virgo/gmwcs/graph/Elem.java +++ b/src/main/java/ru/itmo/ctlab/virgo/gmwcs/graph/Elem.java @@ -61,7 +61,7 @@ public boolean equals(Object o) { @Override public int compareTo(Elem u) { if (u.weight != weight) { - return Double.compare(u.weight, weight); + return Double.compare(weight, u.weight); } return Integer.compare(u.getNum(), num); } diff --git a/src/main/java/ru/itmo/ctlab/virgo/sgmwcs/solver/Dijkstra.java b/src/main/java/ru/itmo/ctlab/virgo/sgmwcs/solver/Dijkstra.java index 40422a1..da606b6 100644 --- a/src/main/java/ru/itmo/ctlab/virgo/sgmwcs/solver/Dijkstra.java +++ b/src/main/java/ru/itmo/ctlab/virgo/sgmwcs/solver/Dijkstra.java @@ -66,6 +66,8 @@ public void solve(Node u) { Set visitedDests = new HashSet<>(); path.put(u, Collections.emptySet()); while ((cur = q.poll()) != null) { + if (visitedDests.contains(cur)) + continue; if (dests.contains(cur) && visitedDests.add(cur) && visitedDests.containsAll(dests)) { diff --git a/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Dijkstra.kt b/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Dijkstra.kt index 69badb4..c58fd5f 100644 --- a/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Dijkstra.kt +++ b/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Dijkstra.kt @@ -6,32 +6,30 @@ import ru.itmo.ctlab.virgo.gmwcs.graph.Elem import ru.itmo.ctlab.virgo.gmwcs.graph.Node import ru.itmo.ctlab.virgo.gmwcs.graph.Graph import java.util.* +import kotlin.math.abs /** * Created by Nikolay Poperechnyi on 04/10/2017. */ -class Dijkstra(private val graph: Graph, private val from: Node, - private val save: Boolean = false) { +class Dijkstra(private val graph: Graph, private val from: Node) { private val s = from.num private val n = graph.vertexSet().maxBy { it.num }!!.num + 1 - private val paths = Array(n, {emptyList()}) + private val visited = BooleanArray(n) { false } - private val visited = BooleanArray(n, { false }) - - private var d = DoubleArray(n, { Double.MAX_VALUE }) + private var d = DoubleArray(n) { Double.MAX_VALUE } private fun solve(neighbors: Set) { if (d[s] != Double.MAX_VALUE) return - val queue = PriorityQueue( - { n1, n2 -> (d[n1.num] - d[n2.num]).compareTo(0) } - ) + val queue = PriorityQueue { n1, n2 -> (d[n1.num] - d[n2.num]).compareTo(0) } d[s] = 0.0 queue.add(from) while (queue.isNotEmpty()) { val cur = queue.poll() + if (visited[cur.num]) + continue visited[cur.num] = true // Stop searching if shortest paths are found if (neighbors.contains(cur) && neighbors.all { visited[it.num] }) @@ -39,7 +37,7 @@ class Dijkstra(private val graph: Graph, private val from: Node, for (adj in graph.neighborListOf(cur).filter { !visited[it.num] }) { // 0 for positive, -weight for negative val e = graph.getEdge(cur, adj) - val ew = if (cur != from) maxOf(p(e, cur), p(e), p(cur)) else p(e) + val ew = p(e, adj) val w = d[cur.num] + ew if (d[adj.num] > w) { d[adj.num] = w @@ -54,14 +52,14 @@ class Dijkstra(private val graph: Graph, private val from: Node, private fun distances(neighbors: Set): Map { solve(neighbors) - return neighbors.map({ Pair(it, d[it.num] + p(from)) }).toMap() + return neighbors.associateWith { d[it.num] + p(from) } } fun negativeEdges(neighbors: Set): List { solve(neighbors) return graph.edgesOf(from).filter { val end = graph.opposite(from, it) - it.weight < 0 && p(it) > d[end.num] + it.weight < 0 && !almostEquals(d[end.num] - p(it), p(end)) // it.weight <= 0 && d[end] < -it.weight } } @@ -69,11 +67,17 @@ class Dijkstra(private val graph: Graph, private val from: Node, fun negativeVertex(dest: Node, candidate: Node): Boolean { solve(setOf(dest)) // test is passed if candidate for removal is not in the solution - val candPathW = d[candidate.num] + p(graph.getEdge(candidate, dest)) + p(candidate) - return d[dest.num] != candPathW + val candPathW = p(graph.getEdge(from, candidate), + graph.getEdge(candidate, dest), + candidate) + return !almostEquals(d[dest.num], candPathW) + } + + private fun almostEquals(a: Double, b: Double): Boolean { + return abs(a - b) < 1e-10 } private fun p(vararg e: Elem): Double { - return -minOf(e.sumByDouble { it.weight }, 0.0) + return -e.sumByDouble { minOf(it.weight, 0.0) } } } \ No newline at end of file diff --git a/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Preprocessor.kt b/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Preprocessor.kt index b9a6074..9bf4a54 100644 --- a/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Preprocessor.kt +++ b/src/main/kotlin/ru/itmo/ctlab/gmwcs/solver/preprocessing/Preprocessor.kt @@ -91,7 +91,7 @@ fun l(graph: Graph, toRemove: MutableNodeSet = mutableSetOf()): NodeSet { continue val e = graph.edgesOf(n).iterator().next() val opposite = graph.opposite(n, e) - if (n.weight + e.weight >= 0) { + if (n.weight + e.weight > 0) { opposite.absorb(n) opposite.absorb(e) toRemove.add(n) @@ -112,12 +112,19 @@ fun mergeNegative(graph: Graph, toRemove: MutableNodeSet = mutableSetOf()): Node continue val l = graph.opposite(v, edges[0]) val r = graph.opposite(v, edges[1]) - toRemove.add(v) //TODO: 2 nodes 1 edge invariant broken here + toRemove.add(v) graph.removeVertex(v) if (l != r) { edges[0].absorb(v) edges[0].absorb(edges[1]) graph.addEdge(l, r, edges[0]) + val es = graph.getAllEdges(l, r).sorted() + val maxE = es[es.size - 1] + for (e in es.subList(0, es.size - 1)) { + if (e.weight >= 0) + maxE.absorb(e) + graph.removeEdge(e) + } } } return toRemove @@ -183,12 +190,12 @@ private fun contract(graph: Graph, e: Edge) { fun negativeVertices(graph: Graph, toRemove: MutableNodeSet = mutableSetOf()): NodeSet { - graph.vertexSet().filterTo(toRemove, { vertexTest(graph, it) }) + graph.vertexSet().filterTo(toRemove) { vertexTest(graph, it) } return toRemove } private fun vertexTest(graph: Graph, v: Node): Boolean { - return if (v.weight <= 0 + return if (v.weight < 0 && graph.neighborListOf(v).size == 2 && graph.edgesOf(v).all { it.weight <= 0 }) { val neighbors = graph.neighborListOf(v) diff --git a/src/test/java/ru/itmo/ctlab/virgo/sgmwcs/SGMWCSTest.java b/src/test/java/ru/itmo/ctlab/virgo/sgmwcs/SGMWCSTest.java index 33ac6f8..755805d 100644 --- a/src/test/java/ru/itmo/ctlab/virgo/sgmwcs/SGMWCSTest.java +++ b/src/test/java/ru/itmo/ctlab/virgo/sgmwcs/SGMWCSTest.java @@ -6,14 +6,16 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; -import ru.itmo.ctlab.virgo.sgmwcs.graph.*; -import ru.itmo.ctlab.virgo.sgmwcs.solver.*; import ru.itmo.ctlab.virgo.SolverException; +import ru.itmo.ctlab.virgo.sgmwcs.graph.Edge; +import ru.itmo.ctlab.virgo.sgmwcs.graph.Graph; +import ru.itmo.ctlab.virgo.sgmwcs.graph.Node; +import ru.itmo.ctlab.virgo.sgmwcs.graph.Unit; +import ru.itmo.ctlab.virgo.sgmwcs.solver.*; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.*; -import java.util.stream.IntStream; import static ru.itmo.ctlab.virgo.sgmwcs.solver.Utils.copy; import static ru.itmo.ctlab.virgo.sgmwcs.solver.Utils.sum; @@ -35,11 +37,11 @@ public class SGMWCSTest { } } - private List tests; - private ComponentSolver solver; - private ReferenceSolver referenceSolver; - private RLTSolver rltSolver; - private Random random; + private final List tests; + private final ComponentSolver solver; + private final ReferenceSolver referenceSolver; + private final RLTSolver rltSolver; + private final Random random; public SGMWCSTest() { random = new Random(SEED); @@ -95,7 +97,7 @@ public void test01_empty() throws SolverException { solver.setLogLevel(0); List res = solver.solve(graph, new Signals()); if (!(res == null || res.isEmpty())) { - Assert.assertTrue("An empty graph can't contain non-empty subgraph", false); + Assert.fail("An empty graph can't contain non-empty subgraph"); } } @@ -305,8 +307,8 @@ private void makeConnectedGraphs(int minSize, int maxSize) { } fillEdgesRandomly(graph, count, nodesArray, edges, size); Map weights = new HashMap<>(); - nodes.forEach(weights::put); - edges.forEach(weights::put); + weights.putAll(nodes); + weights.putAll(edges); tests.add(new TestCase(graph, weights, random)); } } @@ -323,8 +325,8 @@ private void makeUnconnectedGraphs() { Arrays.sort(nodesArray); fillEdgesRandomly(graph, m, nodesArray, edges, 1); Map weights = new HashMap<>(); - nodes.forEach(weights::put); - edges.forEach(weights::put); + weights.putAll(nodes); + weights.putAll(edges); tests.add(new TestCase(graph, weights, random)); } }