Skip to content

Commit b102a3c

Browse files
authored
Merge pull request #16 from QuMuLab/projection
Forgetting/projection.
2 parents d89337c + bf336de commit b102a3c

File tree

2 files changed

+78
-5
lines changed

2 files changed

+78
-5
lines changed

nnf/__init__.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,53 @@ def pair(node: NNF) -> NNF:
998998

999999
return pair(sentence)
10001000

1001+
def project(self, names: 't.Iterable[Name]') -> 'NNF':
1002+
"""Dual of :meth:`forget`: will forget all variables not given"""
1003+
return self.forget(self.vars() - frozenset(names))
1004+
1005+
def forget_aux(self) -> 'NNF':
1006+
"""Returns a theory that forgets all of the auxillary variables"""
1007+
return self.forget(v for v in self.vars() if isinstance(v, Aux))
1008+
1009+
def forget(self, names: 't.Iterable[Name]') -> 'NNF':
1010+
"""Forget a set of variables from the theory.
1011+
1012+
Has the effect of returning a theory without the variables provided,
1013+
such that every model of the new theory has an extension (i.e., an
1014+
assignment) to the forgotten variables that is a model of the original
1015+
theory.
1016+
1017+
:param names: An iterable of the variable names to be forgotten
1018+
"""
1019+
1020+
if self.decomposable():
1021+
return self._forget_with_subs(names)
1022+
else:
1023+
return self._forget_with_shannon(names)
1024+
1025+
def _forget_with_subs(self, names: 't.Iterable[Name]') -> 'NNF':
1026+
1027+
names = frozenset(names)
1028+
1029+
@memoize
1030+
def forget_recurse(node: NNF) -> NNF:
1031+
if isinstance(node, Var) and node.name in names:
1032+
return true
1033+
elif isinstance(node, Var):
1034+
return node
1035+
elif isinstance(node, Internal):
1036+
return node.map(forget_recurse)
1037+
else:
1038+
raise TypeError(node)
1039+
1040+
return forget_recurse(self).simplify()
1041+
1042+
def _forget_with_shannon(self, names: 't.Iterable[Name]') -> 'NNF':
1043+
T = self
1044+
for v in frozenset(names) & self.vars():
1045+
T = T.condition({v: True}) | T.condition({v: False})
1046+
return T.simplify()
1047+
10011048
def deduplicate(self: T_NNF) -> T_NNF:
10021049
"""Return a copy of the sentence without any duplicate objects.
10031050

test_nnf.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,36 @@ def test_iff(a: nnf.NNF, b: nnf.NNF):
733733
c.satisfied_by(model))
734734

735735

736+
@given(NNF())
737+
def test_forget(sentence: nnf.NNF):
738+
# Assumption to reduce the time in testing
739+
assume(sentence.size() <= 15)
740+
741+
# Test that forgetting a backbone variable doesn't change the theory
742+
T = sentence & Var('added_var')
743+
assert sentence.equivalent(T.forget({'added_var'}))
744+
745+
# Test the tseitin projection
746+
assert sentence.equivalent(sentence.to_CNF().forget_aux())
747+
748+
# Test that models of a projected theory are consistent with the original
749+
names = list(sentence.vars())[:2]
750+
T = sentence.forget(names)
751+
assert not any([v in T.vars() for v in names])
752+
753+
for m in T.models():
754+
assert sentence.condition(m).satisfiable()
755+
756+
757+
@given(NNF())
758+
def test_project(sentence: nnf.NNF):
759+
# Test that we get the same as projecting and forgetting
760+
assume(len(sentence.vars()) > 3)
761+
vars1 = list(sentence.vars())[:2]
762+
vars2 = list(sentence.vars())[2:]
763+
assert sentence.forget(vars1).equivalent(sentence.project(vars2))
764+
765+
736766
@given(NNF())
737767
def test_pickling(sentence: nnf.NNF):
738768
new = pickle.loads(pickle.dumps(sentence))
@@ -789,11 +819,7 @@ def test_tseitin(sentence: nnf.NNF):
789819

790820
T = tseitin.to_CNF(sentence)
791821
assert T.is_CNF()
792-
793-
# TODO: Once forgetting/projection is implemented,
794-
# do this more complete check
795-
# aux = filter(lambda x: 'aux' in str(x.name), T.vars())
796-
# assert T.forget(aux).equivalent(sentence)
822+
assert T.forget_aux().equivalent(sentence)
797823

798824
models = list(complete_models(T.models(), sentence.vars() | T.vars()))
799825

0 commit comments

Comments
 (0)