@@ -388,7 +388,11 @@ def implies(self, other: 'NNF') -> bool:
388388 return not self .condition (other .negate ().to_model ()).satisfiable ()
389389 if self .term ():
390390 return not other .negate ().condition (self .to_model ()).satisfiable ()
391- return (self .negate () | other ).valid () # todo: speed this up
391+
392+ if not self .vars () & other .vars ():
393+ return not self .satisfiable () or other .valid ()
394+
395+ return not (self & other .negate ()).satisfiable ()
392396
393397 entails = implies
394398
@@ -458,122 +462,26 @@ def count(node: NNF) -> int:
458462
459463 return sum (1 for _ in sentence .models ())
460464
461- def contradicts (
462- self ,
463- other : 'NNF' ,
464- * ,
465- decomposable : _Tristate = None
466- ) -> bool :
467- """There is no set of values that satisfies both sentences.
468-
469- May be very expensive.
470- """
471- if decomposable is None :
472- decomposable = self .decomposable () and other .decomposable ()
473-
474- if len (self .vars ()) > len (other .vars ()):
475- # The one with the fewest vars has the smallest models
476- a , b = other , self
477- else :
478- a , b = self , other
479-
480- if decomposable :
481- for model in a .models (decomposable = True ):
482- if b ._consistent_with_model (model ):
483- return False
484- return True
465+ def contradicts (self , other : "NNF" ) -> bool :
466+ """There is no set of values that satisfies both sentences."""
467+ if not self .vars () & other .vars ():
468+ return not (self .satisfiable () and other .satisfiable ())
485469
486- for model in b .models ():
487- # Hopefully, a.vars() <= b.vars() and .satisfiable() is fast
488- if a .condition (model ).satisfiable ():
489- return False
490- return True
470+ return not (self & other ).satisfiable ()
491471
492472 def equivalent (self , other : 'NNF' ) -> bool :
493473 """Test whether two sentences have the same models.
494474
495475 If the sentences don't contain the same variables they are
496476 considered equivalent if the variables that aren't shared are
497477 independent, i.e. their value doesn't affect the value of the sentence.
498-
499- Warning: may be very slow. Decomposability helps.
500478 """
501- # TODO: add arguments for decomposability, determinism (per sentence?)
502479 if self == other :
503480 return True
504481
505- models_a = list (self .models ())
506- models_b = list (other .models ())
507- if models_a == models_b :
508- return True
509-
510- vars_a = self .vars ()
511- vars_b = other .vars ()
512- if vars_a == vars_b :
513- if len (models_a ) != len (models_b ):
514- return False
515-
516- Model_T = t .FrozenSet [t .Tuple [Name , bool ]]
517-
518- def dict_hashable (model : t .Dict [Name , bool ]) -> Model_T :
519- return frozenset (model .items ())
520-
521- modset_a = frozenset (map (dict_hashable , models_a ))
522- modset_b = frozenset (map (dict_hashable , models_b ))
523-
524- if vars_a == vars_b :
525- return modset_a == modset_b
526-
527- # There are variables that appear in one sentence but not the other
528- # The sentences can still be equivalent if those variables don't
529- # affect the truth value of the sentence they appear in
530- # That is, for every model that contains such a variable there's
531- # another model with that variable flipped
532-
533- # In such a scenario we can calculate the number of "shared" models in
534- # advance
535- # Each additional variable not shared with the other doubles the
536- # number of models compared to the number of shared models
537- # so we calculate those factors and check whether that works out,
538- # which is much cheaper than actually checking everything, which we
539- # do afterwards if necessary
540- if len (modset_a ) % 2 ** (len (vars_a - vars_b )) != 0 :
541- return False
542- if len (modset_b ) % 2 ** (len (vars_b - vars_a )) != 0 :
543- return False
544- if (len (modset_a ) // 2 ** (len (vars_a - vars_b )) !=
545- len (modset_b ) // 2 ** (len (vars_b - vars_a ))):
546- return False
547-
548- def flip (model : Model_T , var : Name ) -> Model_T :
549- other = (var , False ) if (var , True ) in model else (var , True )
550- return (model - {(var , False ), (var , True )}) | {other }
551-
552- modset_a_small = set () # type: t.Set[Model_T]
553- for model in modset_a :
554- for var in vars_a - vars_b :
555- if flip (model , var ) not in modset_a :
556- return False
557- true_vars = {(var , True ) for var in vars_a - vars_b }
558- if true_vars <= model : # Don't add it multiple times
559- modset_a_small .add (model - true_vars )
560-
561- modset_b_small = set () # type: t.Set[Model_T]
562- for model in modset_b :
563- for var in vars_b - vars_a :
564- if flip (model , var ) not in modset_b :
565- return False
566- true_vars = {(var , True ) for var in vars_b - vars_a }
567- if true_vars <= model :
568- modset_b_small .add (model - true_vars )
569-
570- if modset_a_small != modset_b_small :
571- return False
572-
573- assert (len (modset_a_small )
574- == len (modset_a ) // 2 ** (len (vars_a - vars_b )))
575-
576- return True
482+ return not (
483+ (self & other .negate ()) | (self .negate () & other )
484+ ).satisfiable ()
577485
578486 def negate (self ) -> 'NNF' :
579487 """Return a new sentence that's true iff the original is false."""
0 commit comments