Skip to content

Add Implication and Remove Nesting #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 23, 2021
29 changes: 29 additions & 0 deletions nnf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,32 @@ class NNF(metaclass=abc.ABCMeta):

def __and__(self: T_NNF, other: U_NNF) -> 'And[t.Union[T_NNF, U_NNF]]':
"""And({self, other})"""
# prevent unnecessary nesting
if config.auto_simplify:
if not isinstance(self, And) and isinstance(other, And):
return And({self, *other.children})
elif isinstance(self, And) and not isinstance(other, And):
return And({*self.children, other})
elif isinstance(self, And) and isinstance(other, And):
return And({*self.children, *other.children})
return And({self, other})

def __or__(self: T_NNF, other: U_NNF) -> 'Or[t.Union[T_NNF, U_NNF]]':
"""Or({self, other})"""
# prevent unnecessary nesting
if config.auto_simplify:
if not isinstance(self, Or) and isinstance(other, Or):
return Or({self, *other.children})
elif isinstance(self, Or) and not isinstance(other, Or):
return Or({*self.children, other})
elif isinstance(self, Or) and isinstance(other, Or):
return Or({*self.children, *other.children})
return Or({self, other})

def __rshift__(self, other: 'NNF') -> 'Or[NNF]':
"""Or({self.negate(), other})"""
return Or({self.negate(), other})

def walk(self) -> t.Iterator['NNF']:
"""Yield all nodes in the sentence, depth-first.

Expand Down Expand Up @@ -1788,6 +1808,7 @@ class _Config:
sat_backend = _Setting("auto", {"auto", "native", "kissat", "pysat"})
models_backend = _Setting("auto", {"auto", "native", "pysat"})
pysat_solver = _Setting("minisat22")
auto_simplify = _Setting(False, {True, False})

__slots__ = ()

Expand Down Expand Up @@ -1839,6 +1860,14 @@ def __call__(self, **settings: str) -> _ConfigContext:
#: `pysat.solvers.SolverNames
#: <https://pysathq.github.io/docs/html/api/solvers.html
#: #pysat.solvers.SolverNames>`_. Default: ``minisat22``.
#:
#: - ``auto_simplify``: An optional setting to prevent "extra" nesting when
#: NNF formulas are added onto with & or |.
#:
#: - ``True``: Remove nesting whenever & or | are used on an NNF formula.
#: - ``False``: Generate formulas as normal, with nesting reflecting exactly
#: how the formula was created.

config = _Config()


Expand Down
82 changes: 82 additions & 0 deletions test_nnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1105,3 +1105,85 @@ def test_models(sentence: nnf.NNF):
models = list(sentence.models())
assert len(real_models) == len(models)
assert model_set(real_models) == model_set(models)


@config(auto_simplify=False)
def test_nesting():
a, b, c, d, e, f = Var("a"), Var("b"), Var("c"), Var("d"), \
Var("e"), Var("f")

# test left nestings on And
config.auto_simplify = False
formula = a & (b & c)
formula = formula & (d | e)
assert formula == And({And({And({b, c}), a}), Or({d, e})})
config.auto_simplify = True
formula = a & (b & c)
formula = formula & (d | e)
assert formula == And({a, b, c, Or({d, e})})

# test right nestings on And
config.auto_simplify = False
formula = a & (b & c)
formula = (d | e) & formula
assert formula == And({And({And({b, c}), a}), Or({d, e})})
config.auto_simplify = True
formula = a & (b & c)
formula = (d | e) & formula
assert formula == And({a, b, c, Or({d, e})})

# test nestings on both sides with And
config.auto_simplify = False
formula = a & (b & c)
formula2 = d & (e & f)
formula = formula & formula2
assert formula == And({(And({a, And({b, c})})), And({d, And({e, f})})})
config.auto_simplify = True
formula = a & (b & c)
formula2 = d & (e & f)
formula = formula & formula2
assert formula == And({a, b, c, d, e, f})

# test left nestings on Or
config.auto_simplify = False
formula = a | (b | c)
formula = formula | (d & e)
assert formula == Or({Or({Or({b, c}), a}), And({d, e})})
config.auto_simplify = True
formula = a | (b | c)
formula = formula | (d & e)
assert formula == Or({a, b, c, And({d, e})})

# test right nestings on Or
config.auto_simplify = False
formula = a | (b | c)
formula = (d & e) | formula
assert formula == Or({Or({Or({b, c}), a}), And({d, e})})
config.auto_simplify = True
formula = a | (b | c)
formula = (d & e) | formula
assert formula == Or({a, b, c, And({d, e})})

# test nestings on both sides with Or
config.auto_simplify = False
formula = a | (b | c)
formula2 = d | (e | f)
formula = formula | formula2
assert formula == Or({(Or({a, Or({b, c})})), Or({d, Or({e, f})})})
config.auto_simplify = True
formula = a | (b | c)
formula2 = d | (e | f)
formula = formula | formula2
assert formula == Or({a, b, c, d, e, f})

# test nestings with both And and Or
config.auto_simplify = False
formula = a & (b | c)
formula2 = d & (e & f)
formula = formula | formula2
assert formula == Or({(And({a, Or({b, c})})), And({d, And({e, f})})})
config.auto_simplify = True
formula = a & (b | c)
formula2 = d & (e & f)
formula = formula | formula2
assert formula == Or({(And({a, Or({b, c})})), And({d, e, f})})