Skip to content

Commit

Permalink
xor decomposition using sum
Browse files Browse the repository at this point in the history
* recursive xor using sum.
  • Loading branch information
Wout4 authored Oct 17, 2023
1 parent 4651fe4 commit b2e7388
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 10 deletions.
15 changes: 5 additions & 10 deletions cpmpy/expressions/globalconstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,11 @@ def __init__(self, arg_list):
super().__init__("xor", flatargs)

def decompose(self):
# there are multiple decompositions possible
# sum(args) mod 2 == 1, for size 2: sum(args) == 1
# since Xor is logical constraint, the default is a logic decomposition
a0, a1 = self.args[:2]
cons = (a0 | a1) & (~a0 | ~a1) # one true and one false

# for more than 2 variables, we cascade (decomposed) xors
for arg in self.args[2:]:
cons = (cons | arg) & (~cons | ~arg)
return [cons], []
# there are multiple decompositions possible, Recursively using sum allows it to be efficient for all solvers.
decomp = [sum(self.args[:2]) == 1]
if len(self.args) > 2:
decomp = Xor([decomp,self.args[2:]]).decompose()[0]
return decomp, []

def value(self):
return sum(argval(a) for a in self.args) % 2 == 1
Expand Down
2 changes: 2 additions & 0 deletions tests/test_globalconstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ def test_not_xor(self):
self.assertFalse(cp.Xor(bv).value())
nbNotModels = cp.Model(~cp.Xor(bv)).solveAll(display=lambda: self.assertFalse(cp.Xor(bv).value()))
nbModels = cp.Model(cp.Xor(bv)).solveAll(display=lambda: self.assertTrue(cp.Xor(bv).value()))
nbDecompModels = cp.Model(cp.Xor(bv).decompose()).solveAll(display=lambda: self.assertTrue(cp.Xor(bv).value()))
self.assertEqual(nbDecompModels,nbModels)
total = cp.Model(bv == bv).solveAll()
self.assertEqual(str(total), str(nbModels + nbNotModels))

Expand Down

0 comments on commit b2e7388

Please sign in to comment.