Skip to content

Commit 2037f12

Browse files
committed
WIP: [py] Make single interface for polynomials
1 parent 51f7536 commit 2037f12

File tree

10 files changed

+430
-6
lines changed

10 files changed

+430
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ catch.hpp
33
__pycache__
44
tags
55
*.bak
6+
*.pyc

symbsat-py/symbsat/poly.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ def __str__(self):
5353
def copy(self):
5454
return Poly(self)
5555

56-
@classmethod
57-
def zero(cls):
58-
return cls([])
56+
def zero(self):
57+
class_ = type(self)
58+
return class_([])
5959

60-
@classmethod
61-
def one(cls):
62-
return cls([Monom.one()])
60+
def one(self):
61+
class_ = type(self)
62+
return class_([Monom.one()])
6363

6464
def is_zero(self):
6565
return self == []
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .list import PolyList # noqa
2+
from .zdd import PolyZDD # noqa

symbsat-py/symbsat/poly_new/base.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import abc
2+
3+
4+
class Poly(metaclass=abc.ABCMeta):
5+
"""Base class for boolean polynomials."""
6+
7+
def __init__(self, *, monoms=None, var=None, monom=None):
8+
is_monoms = monoms is not None
9+
is_var = var is not None
10+
is_monom = monom is not None
11+
12+
# We want zero or one parameter
13+
if [is_monoms, is_var, is_monom].count(True) > 1:
14+
raise RuntimeError("Only one initialization parameter is allowed")
15+
16+
if is_monoms:
17+
self._init_monoms(monoms)
18+
elif is_var:
19+
self._init_var(var)
20+
elif is_monom:
21+
self._init_monom(monom)
22+
else:
23+
raise NotImplementedError
24+
25+
@abc.abstractclassmethod
26+
def make_zero(cls):
27+
raise NotImplementedError
28+
29+
@abc.abstractclassmethod
30+
def make_one(cls):
31+
raise NotImplementedError
32+
33+
@abc.abstractmethod
34+
def _init_monoms(self, monoms):
35+
raise NotImplementedError
36+
37+
@abc.abstractmethod
38+
def _init_var(self, var):
39+
raise NotImplementedError
40+
41+
@abc.abstractmethod
42+
def _init_monom(self, monom):
43+
raise NotImplementedError
44+
45+
@abc.abstractmethod
46+
def __add__(self, other):
47+
raise NotImplementedError
48+
49+
@abc.abstractmethod
50+
def __mul__(self, other):
51+
raise NotImplementedError
52+
53+
@abc.abstractmethod
54+
def __str__(self):
55+
raise NotImplementedError
56+
57+
@abc.abstractmethod
58+
def copy(self):
59+
raise NotImplementedError
60+
61+
@abc.abstractmethod
62+
def is_zero(self):
63+
raise NotImplementedError
64+
65+
@abc.abstractmethod
66+
def is_one(self):
67+
raise NotImplementedError
68+
69+
@abc.abstractmethod
70+
def lm(self):
71+
raise NotImplementedError

symbsat-py/symbsat/poly_new/list.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import collections
2+
import itertools
3+
import operator
4+
5+
from symbsat.monom import Monom
6+
7+
from .base import Poly
8+
9+
10+
class PolyList(Poly):
11+
12+
def _init_monoms(self, monoms):
13+
self._list = sorted(monoms, reverse=True)
14+
15+
def _init_var(self, var):
16+
self._list = [Monom(vars=[var])]
17+
18+
def _init_monom(self, monom):
19+
self._list = [monom]
20+
21+
@classmethod
22+
def make_zero(cls):
23+
raise NotImplementedError
24+
25+
@classmethod
26+
def make_one(cls):
27+
raise NotImplementedError
28+
29+
def __add__(self, other):
30+
# symmetric difference
31+
if isinstance(other, Monom):
32+
return PolyList(monoms=list(set(self) ^ set([other])))
33+
if isinstance(other, PolyList):
34+
return PolyList(monoms=list(set(self) ^ set(other)))
35+
return NotImplemented
36+
37+
def __mul__(self, other):
38+
if isinstance(other, Monom):
39+
if self.is_zero() or other.is_zero():
40+
return PolyList.make_zero()
41+
monoms = map(lambda m: m*other, self)
42+
elif isinstance(other, PolyList):
43+
if self.is_zero() or other.is_zero():
44+
return PolyList.make_zero()
45+
monoms = itertools.starmap(
46+
operator.mul,
47+
(itertools.product(self, other))
48+
)
49+
else:
50+
return NotImplemented
51+
52+
counter = collections.Counter(monoms)
53+
return PolyList(monoms={m for m, c in counter.items() if c % 2 != 0})
54+
55+
def __str__(self):
56+
raise NotImplementedError
57+
58+
def copy(self):
59+
raise NotImplementedError
60+
61+
def is_zero(self):
62+
raise NotImplementedError
63+
64+
def is_one(self):
65+
raise NotImplementedError
66+
67+
def lm(self):
68+
raise NotImplementedError

symbsat-py/symbsat/poly_new/zdd.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from symbsat.zdd_new import ZDD
2+
from .base import Poly
3+
4+
5+
class PolyZDD(Poly):
6+
7+
@classmethod
8+
def make_zero(cls):
9+
raise NotImplementedError
10+
11+
@classmethod
12+
def make_one(cls):
13+
raise NotImplementedError
14+
15+
def _init_var(self, var: int):
16+
self._zdd = ZDD(var)
17+
18+
def _init_monom(self, monom):
19+
if monom.is_zero():
20+
self._zdd = ZDD.zero()
21+
elif monom.is_one():
22+
self._zdd = ZDD.one()
23+
else:
24+
self._zdd = ZDD(monom.vars[0])
25+
for var in monom.vars[1:]:
26+
self._zdd *= ZDD(var)
27+
28+
def _init_monoms(self, monoms):
29+
raise NotImplementedError
30+
31+
def __add__(self, other):
32+
raise NotImplementedError
33+
34+
def __mul__(self, other):
35+
raise NotImplementedError
36+
37+
def __imul__(self, other):
38+
raise NotImplementedError
39+
40+
def __str__(self):
41+
raise NotImplementedError
42+
43+
def copy(self):
44+
raise NotImplementedError
45+
46+
def is_zero(self):
47+
raise NotImplementedError
48+
49+
def is_one(self):
50+
raise NotImplementedError
51+
52+
def lm(self):
53+
raise NotImplementedError
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .zdd import ZDD

symbsat-py/symbsat/zdd_new/node.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
class Node:
2+
3+
__slots__ = ('var', 'mul', 'add')
4+
5+
def __init__(self, var, m, a):
6+
if m and m.is_zero():
7+
raise TypeError('Multiply branch cannot be zero')
8+
9+
self.var = var
10+
self.mul = m # and
11+
self.add = a # xor
12+
13+
@classmethod
14+
def zero(cls):
15+
return cls(-2, None, None)
16+
17+
@classmethod
18+
def one(cls):
19+
return cls(-1, None, None)
20+
21+
def copy(self):
22+
if self.var < 0:
23+
return self
24+
return Node(
25+
self.var,
26+
self.mul.copy(),
27+
self.add.copy()
28+
)
29+
30+
def is_zero(self):
31+
return self.var == -2
32+
33+
def is_one(self):
34+
return self.var == -1
35+
36+
def __str__(self):
37+
if self.is_one():
38+
return "_one"
39+
if self.is_zero():
40+
return "_zero"
41+
42+
return (
43+
'%s -> {%s} {%s}' %
44+
(self.var, self.mul, self.add)
45+
)
46+
47+
def __eq__(self, other):
48+
return id(self) == id(other)

symbsat-py/symbsat/zdd_new/zdd.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from .node import Node
2+
3+
4+
class ZDD:
5+
6+
def __init__(self, var=-1):
7+
if var < 0:
8+
self.root = Node.zero()
9+
else:
10+
self.root = self.create_node(
11+
var, Node.one(), Node.zero()
12+
)
13+
14+
def create_node(self, var: int, m: Node, a: Node):
15+
return Node(var, m, a)
16+
17+
@classmethod
18+
def zero(cls):
19+
return cls()
20+
21+
@classmethod
22+
def one(cls):
23+
zdd = cls()
24+
zdd.root = Node.one()
25+
return zdd
26+
27+
def _add(self, i, j):
28+
29+
if i.is_zero():
30+
return j
31+
if j.is_zero():
32+
return i
33+
if i == j:
34+
return self.zero()
35+
36+
if i.is_one():
37+
r = self.create_node(
38+
j.var, j.mul, self._add(j.add, Node.one())
39+
)
40+
elif j.is_one():
41+
r = self.create_node(
42+
i.var, i.mul, self._add(i.add, Node.one())
43+
)
44+
else:
45+
if i.var < j.var:
46+
r = self.create_node(i.var, i.mul, self._add(i.add, j))
47+
elif i.var > j.var:
48+
r = self.create_node(j.var, j.mul, self._add(i, j.add))
49+
else:
50+
m = self._add(i.mul, j.mul)
51+
a = self._add(i.add, j.add)
52+
53+
if m.is_zero():
54+
return a
55+
56+
r = self.create_node(i.var, m, a)
57+
return r
58+
59+
def _mul(self, i, j):
60+
61+
if i.is_one():
62+
return j
63+
if i.is_zero() or j.is_zero():
64+
return Node.zero()
65+
if j.is_one() or i == j:
66+
return i
67+
68+
r = None
69+
if i.var < j.var:
70+
m = self._mul(i.mul, j)
71+
a = self._mul(i.add, j)
72+
73+
if m.is_zero():
74+
return a
75+
76+
r = self.create_node(i.var, m, a)
77+
elif i.var > j.var:
78+
m = self._mul(j.mul, i)
79+
a = self._mul(j.add, i)
80+
81+
if m.is_zero():
82+
return a
83+
84+
r = self.create_node(j.var, m, a)
85+
else:
86+
m1 = self._mul(i.add, j.mul)
87+
m2 = self._mul(i.mul, j.mul)
88+
m3 = self._mul(i.mul, j.add)
89+
ms_sum = self._add(m1, self._add(m2, m3))
90+
91+
if ms_sum.is_zero():
92+
return self._mul(i.add, j.add)
93+
94+
r = self.create_node(i.var, ms_sum, self._mul(i.add, j.add))
95+
return r
96+
97+
def __mul__(self, other):
98+
self_cls = type(self)
99+
zdd = self_cls()
100+
zdd.root = zdd._mul(self.root, other.root)
101+
return zdd
102+
103+
def __add__(self, other):
104+
self_cls = type(self)
105+
zdd = self_cls()
106+
zdd.root = zdd._add(self.root, other.root)
107+
return zdd
108+

0 commit comments

Comments
 (0)