Skip to content

Instantly share code, notes, and snippets.

@rvprasad
Last active April 16, 2018 03:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rvprasad/a79e343c9a644a9d96c268f29600ce26 to your computer and use it in GitHub Desktop.
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
/*
* 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