-
Notifications
You must be signed in to change notification settings - Fork 0
/
encodings.nim
92 lines (77 loc) · 3.07 KB
/
encodings.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import cnf
import sequtils
import utils
proc atLeastOne*(literals: seq[Literal]): seq[Clause] = @[literals]
proc atMostOnePairwise*(literals: seq[Literal]): seq[Clause] =
result = newSeq[Clause]()
for i in 0..<literals.len:
for j in i+1..<literals.len:
result.add(@[-literals[i], -literals[j]])
proc getCommanders(binaries: seq[Literal], index: int): seq[Literal] =
var power = 1
result = newSeq[Literal]()
for i in 0..<binaries.len:
if (index and power) == power:
result.add(binaries[i])
else:
result.add(-binaries[i])
power = power shl 1
proc exactlyOne*(cnf: var Cnf, literals: seq[Literal], pairwise: bool = false): seq[Clause]
proc atMostOneCommander*(cnf: var Cnf, literals: seq[Literal]): seq[Clause] =
# Commander Encoding [Klieber and Kwon]
if literals.len < 6:
return atMostOnePairwise(literals)
let m = literals.len div 3
let groups = literals.distribute(m)
let commanders = toSeq(cnf.numLiterals+1..cnf.numLiterals+groups.len)
cnf.addLiterals(groups.len)
result = newSeq[Clause]()
# at most one variable in a group can be true
for group in groups:
result.add(atMostOnePairWise(group))
for zipped in zip(commanders, groups):
let (commander, group) = zipped
let commanderAndGroup = @[-commander].concat(group)
result.add(atLeastOne(commanderAndGroup))
result.add(atMostOnePairwise(commanderAndGroup))
if commanders.len >= 3:
result.add(cnf.exactlyOne(commanders))
else:
result.add(cnf.exactlyOne(commanders, pairwise=true))
proc getGroupSize(n: int): int =
let limit = n div 2
result = 1
while (result shl 1) - 1 <= limit:
result = result shl 1
result -= 1
# Not used for the moment since it's somehow takes too long
# to solve with this encoding
proc atMostOne*(cnf: var Cnf, literals: seq[Literal]): seq[Clause] =
# Bimander Encoding [Hölldobler and Nguyen, 2013]
let m = getGroupSize(literals.len)
let numDigits = log2(m)
if literals.len <= m:
return atMostOnePairwise(literals)
let groups = literals.distribute(m)
let binaries = toSeq(cnf.numLiterals+1..cnf.numLiterals+numDigits)
cnf.addLiterals(numDigits)
result = newSeq[Clause]()
# at most one variable in a group can be true
for group in groups:
result.add(atMostOnePairWise(group))
# constraints between each variable in a group and commander variables
for pair in groups.pairs():
let (index, group) = pair
let commanders = binaries.getCommanders(index)
for literal in group:
for commander in commanders:
result.add(@[-literal, commander])
proc exactlyOne*(cnf: var Cnf, literals: seq[Literal], pairwise: bool = false): seq[Clause] =
if literals.len == 0: return @[]
result = newSeq[Clause]()
if not pairwise:
# result.add(cnf.atMostOne(literals))
result.add(cnf.atMostOneCommander(literals))
else:
result.add(atMostOnePairwise(literals))
result.add(atLeastOne(literals))