@@ -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
0 commit comments