Skip to content

Commit

Permalink
Merge pull request #44 from pysmt/cone_walker
Browse files Browse the repository at this point in the history
Get free Variables
  • Loading branch information
mikand committed Mar 13, 2015
2 parents 880ffae + 8e00e18 commit abf1f15
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 76 deletions.
15 changes: 12 additions & 3 deletions docs/CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
Change Log
==========

0.2.4 XXXXXXXX -- YYYY
-----------------------

General:

* Iterative implementation of FNode.get_free_variables().
This also deprecates FNode.get_dependencies().


0.2.3 2015-03-12 -- Logics Refactoring
--------------------------------------

General:

* install.py: script to automate the installation of supported
solvers.
solvers.

* get_logic() Oracle: Detects the logic used in a formula. This can now be used in the shortcuts (_is_sat()_, _is_unsat()_, _is_valid()_, and
_get_model()_) by choosing the special logic pysmt.logics.AUTO.

Expand All @@ -24,7 +33,7 @@ General:
* The default logic for Factory.get_solver() is now the most generic
*quantifier free* logic supported by pySMT (currently,
QF_UFLIRA). The factory not provides a way to change this default.

* Removed option _quantified_ from all shortcuts.


Expand Down
6 changes: 6 additions & 0 deletions pysmt/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(self):
self._serializer = pysmt.printers.HRSerializer(self)
self._qfo = pysmt.oracles.QuantifierOracle(self)
self._theoryo = pysmt.oracles.TheoryOracle(self)
self._fvo = pysmt.oracles.FreeVarsOracle(self)

self._factory = None
# Configurations
Expand Down Expand Up @@ -86,6 +87,11 @@ def theoryo(self):
""" Get the Theory Oracle """
return self._theoryo

@property
def fvo(self):
""" Get the FreeVars Oracle """
return self._fvo

def add_dynamic_walker_function(self, nodetype, walker, function):
"""Dynamically bind the given function to the walker for the nodetype.
Expand Down
45 changes: 6 additions & 39 deletions pysmt/fnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
# limitations under the License.
#
"""FNode are the building blocks of formulae."""

import collections

import shortcuts
from pysmt.operators import ALL_TYPES, QUANTIFIERS, CONSTANTS
from pysmt.operators import CONSTANTS
from pysmt.operators import (FORALL, EXISTS, AND, OR, NOT, IMPLIES, IFF,
SYMBOL, FUNCTION,
REAL_CONSTANT, BOOL_CONSTANT, INT_CONSTANT,
Expand All @@ -29,15 +28,11 @@
ITE,
TOREAL)
from pysmt.typing import BOOL, REAL, INT, PYSMT_TYPES
from pysmt.decorators import deprecated

FNodeContent = collections.namedtuple("FNodeContent",
["node_type", "args", "payload"])

# Operators for which Args is an FNode (used by compute_dependencies
DEPENDENCIES_SIMPLE_ARGS = (set(ALL_TYPES) - \
(set([SYMBOL, FUNCTION]) | QUANTIFIERS | CONSTANTS))


class FNode(object):
r"""FNode represent the basic structure for representing a formula.
Expand All @@ -55,7 +50,6 @@ class FNode(object):

def __init__(self, content):
self._content = content
self._dependencies = None
return

# __eq__ and __hash__ are left as default
Expand All @@ -70,43 +64,16 @@ def args(self):
def arg(self, idx):
return self._content.args[idx]


@deprecated("get_free_variables")
def get_dependencies(self):
if self._dependencies is None:
self._dependencies = self._compute_dependencies()
return self._dependencies

def _compute_dependencies(self):
if self.node_type() in DEPENDENCIES_SIMPLE_ARGS:
res = set()
for s in self.get_sons():
res.update(s.get_dependencies())
return res

elif self.node_type() in QUANTIFIERS:
return self.arg(0).get_dependencies().difference(self._content.payload)

elif self.node_type() == SYMBOL:
return frozenset([self])

elif self.node_type() in CONSTANTS:
return frozenset()

elif self.node_type() == FUNCTION:
res = set([self._content.payload])
for p in self.args():
res.update(p.get_dependencies())
return res

else:
assert False
return
return self.get_free_variables()

def get_free_variables(self):
return shortcuts.get_free_variables(self)

def get_sons(self):
return self.args()


def simplify(self):
return shortcuts.simplify(self)

Expand Down
65 changes: 64 additions & 1 deletion pysmt/oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* QuantifierOracle says whether a formula is quantifier free
* TheoryOracle says which logic is used in the formula.
* FreeVarsOracle says which variables are free in the formula
"""

import pysmt.walkers as walkers
Expand Down Expand Up @@ -168,10 +169,72 @@ def get_theory(self, formula):
"""Returns the thoery for the formula."""
return self.walk(formula)


# EOC TheoryOracle


# Operators for which Args is an FNode
DEPENDENCIES_SIMPLE_ARGS = (set(op.ALL_TYPES) - \
(set([op.SYMBOL, op.FUNCTION]) | op.QUANTIFIERS | op.CONSTANTS))

class FreeVarsOracle(pysmt.walkers.DagWalker):
def __init__(self, env=None):
pysmt.walkers.DagWalker.__init__(self, env=env)

# Clear the mapping function
self.functions.clear()

# We have only few categories for this walker.
#
# - Simple Args simply need to combine the cone/dependencies
# of the children.
# - Quantifiers need to exclude bounded variables
# - Constants have no impact
#
for elem in op.ALL_TYPES:
if elem in DEPENDENCIES_SIMPLE_ARGS:
self.functions[elem] = self.walk_simple_args
elif elem in op.QUANTIFIERS:
self.functions[elem] = self.walk_quantifier
elif elem in op.CONSTANTS:
self.functions[elem] = self.walk_constant
else:
self.functions[elem] = self.walk_error

# These are the only 2 cases that can introduce elements.
self.functions[op.SYMBOL] = self.walk_symbol
self.functions[op.FUNCTION] = self.walk_function

# Check that no operator in undefined
assert self.is_complete(verbose=True)


def get_free_variables(self, formula):
"""Returns the set of Symbols appearing free in the formula."""
return self.walk(formula)

def walk_simple_args(self, formula, args):
res = set()
for arg in args:
res.update(arg)
return frozenset(res)

def walk_quantifier(self, formula, args):
return args[0].difference(formula.quantifier_vars())

def walk_symbol(self, formula, args):
return frozenset([formula])

def walk_constant(self, formula, args):
return frozenset()

def walk_function(self, formula, args):
res = set([formula.function_name()])
for arg in args:
res.update(arg)
return frozenset(res)

# EOC FreeVarsOracle

def get_logic(formula, env=None):
if env is None:
env = pysmt.shortcuts.get_env()
Expand Down
4 changes: 4 additions & 0 deletions pysmt/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ def serialize(formula, threshold=None):
return get_env().serializer.serialize(formula,
threshold=threshold)

def get_free_variables(formula):
"""Returns the simplified version of the formula."""
return get_env().fvo.get_free_variables(formula)

##### Nodes Creation #####

def ForAll(variables, formula):
Expand Down
4 changes: 2 additions & 2 deletions pysmt/simplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def walk_forall(self, formula, args):
assert len(args) == 1
sf = args[0]

varset = set(formula.quantifier_vars()).intersection(sf.get_dependencies())
varset = set(formula.quantifier_vars()).intersection(sf.get_free_variables())

if len(varset) == 0:
return sf
Expand All @@ -260,7 +260,7 @@ def walk_exists(self, formula, args):
assert len(args) == 1
sf = args[0]

varset = set(formula.quantifier_vars()).intersection(sf.get_dependencies())
varset = set(formula.quantifier_vars()).intersection(sf.get_free_variables())

if len(varset) == 0:
return sf
Expand Down
2 changes: 1 addition & 1 deletion pysmt/smtlib/printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def _push_with_children_to_stack(self, formula, **kwargs):
def printer(self, f):
self.openings = 0
self.name_seed = 0
self.names = set(x.symbol_name() for x in f.get_dependencies())
self.names = set(x.symbol_name() for x in f.get_free_variables())

key = self.walk(f)
self.write(key)
Expand Down
2 changes: 1 addition & 1 deletion pysmt/smtlib/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def smtlibscript_from_formula(formula):
script.add(name=smtcmd.SET_LOGIC,
args=[UFLIRA])

deps = formula.get_dependencies()
deps = formula.get_free_variables()
# Declare all variables
for symbol in deps:
assert symbol.is_symbol()
Expand Down
2 changes: 1 addition & 1 deletion pysmt/smtlib/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def reset_assertions(self):
return

def add_assertion(self, formula, named=None):
deps = formula.get_dependencies()
deps = formula.get_free_variables()
for d in deps:
if d not in self.declared_vars:
self._declare_variable(d)
Expand Down
2 changes: 1 addition & 1 deletion pysmt/substituter.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _push_with_children_to_stack(self, formula, **kwargs):
# we do not consider this substitution in the body of
# the quantifier.
if all(m not in formula.quantifier_vars()
for m in k.get_dependencies()):
for m in k.get_free_variables()):
new_subs[k] = v

# 2. We apply the substitution on the quantifier body with
Expand Down

0 comments on commit abf1f15

Please sign in to comment.