Last active
April 16, 2018 03:14
-
-
Save rvprasad/a79e343c9a644a9d96c268f29600ce26 to your computer and use it in GitHub Desktop.
Identifies a DFS traversal order of nodes of a given directed graph using z3 solver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2018, Venkatesh-Prasad Ranganath | |
* | |
* Licensed under BSD 3-clause License | |
* | |
* Author: Venkatesh-Prasad Ranganath | |
*/ | |
import groovy.transform.Field | |
// Identifies a DFS traversal order of nodes of a given directed graph | |
// using SAT solving | |
// Script uses z3 solver (https://github.com/Z3Prover/z3) | |
final cli = new CliBuilder(usage:"groovy dfsSolver.groovy") | |
cli.s(longOpt:'z3Path', args:1, argName:'z3Path', required:true, | |
'Path to z3') | |
cli.g(longOpt:'graph', args:1, argName:'graph', required:true, | |
'Graph to traverse. Each line contains an edge in "n1,n2" format') | |
final options = cli.parse(args) | |
if (!options) { | |
return | |
} | |
// Output is an ordering of |N| nodes | |
// Each node occurs in at least one position | |
// Each node occurs in at most one position | |
// Each position has at least one node | |
// For each edge (m, n), if m@g, n@h, and g<h, | |
// then, for each node q@j such that g<j<h, there exists an edge (p, q) such | |
// that p@i and g<=i<j | |
final edges = new File(options.g).readLines().collect { | |
it.split(',') as List | |
} | |
final nodes = edges.collectMany { it }.unique() | |
final positions = 1..nodes.size() | |
@Field final varMap = { | |
final tmp1 = [:] | |
tmp1.withDefault { tmp1.size() + 1 } | |
}.call() | |
@Field final nodePos2var = [:].withDefault { varMap[it] } | |
def getVarForNodePos(n, p) { nodePos2var["$n,$p"] } | |
final formula = [] | |
// Each node occurs in at least one position | |
final subFormula1 = nodes.collect { n -> | |
positions.collect { l -> getVarForNodePos(n, l) } | |
} | |
formula.addAll(subFormula1) | |
// Each node occurs in at most one position | |
final subFormula2 = [nodes, positions].combinations() | |
.collectMany { n, l -> | |
final v1 = getVarForNodePos(n, l) | |
positions.findAll { it > l }.collect { k -> | |
[-v1, -getVarForNodePos(n, k)] | |
} | |
} | |
formula.addAll(subFormula2) | |
// Each position has at most one node | |
final subFormula3 = [positions, nodes].combinations().collectMany { g, n -> | |
final nAtg = getVarForNodePos(n, g) | |
nodes.findAll { it != n }.collect { m -> | |
[-nAtg, -getVarForNodePos(m, g)] | |
} | |
} | |
formula.addAll(subFormula3) | |
// For each edge (m, n), if m@g, n@h, and g<h, | |
// then, for each node q@j such that g<j<h, there exists an edge (p, q) such | |
// that p@i and g<=i<j | |
List combinations(List list) { | |
list.any { it.empty } ? [] : list.combinations() | |
} | |
final subFormula4 = edges.collectMany { m, n -> | |
[positions, positions].combinations().findAll { it[0] < it[1] } | |
.collectMany { g, h -> | |
final mAtg = getVarForNodePos(m, g) | |
final nAth = getVarForNodePos(n, h) | |
combinations([nodes.findAll { it != m && it != n }, | |
positions.findAll { it > g && it < h }]) | |
.collect { q, j -> | |
final qAtj = getVarForNodePos(q, j) | |
final tmp1 = edges.findAll { it[1] == q }.collect { it[0] } | |
final tmp2 = positions.findAll { it >= g && it < j } | |
final tmp3 = combinations([tmp1, tmp2]) | |
[-mAtg, -nAth, -qAtj] + | |
tmp3.collect { p, i -> getVarForNodePos(p, i) } | |
} | |
} | |
} | |
formula.addAll(subFormula4) | |
// Pick a random starting node | |
final startNode = nodes[new Random().nextInt(nodes.size())] | |
formula.add([getVarForNodePos(startNode, 1)]) | |
final file1 = new File("dfsGraphOrder.cnf") | |
file1.withPrintWriter { writer -> | |
final tmp1 = formula.unique { it.sort().join() } | |
writer.println "p cnf ${nodePos2var.size()} ${tmp1.size()}" | |
tmp1.each { | |
writer.println it.join(' ') + ' 0' | |
} | |
} | |
final process1 = ("${options.s} " + file1.name).execute() | |
final outStream1 = process1.getInputStream() | |
process1.waitFor() | |
final output1 = outStream1.readLines() | |
if (output1[0] == 'unsat') { | |
println 'unsat' | |
System.exit(0) | |
} | |
final var2nodePos = nodePos2var.collectEntries { k, v -> [v, k] } | |
println "Node,Pos" | |
output1[1].split(' ').collect { it as int }.findAll { it in var2nodePos } | |
.collect { var2nodePos[it].split(',') }.sort { it[1] as int } | |
.each { println it.join(',') } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment