def is_literal(f):
    return isinstance(f, atoms.Atom) or (isinstance(f, connectives.Not) and is_literal(f.formulas[0])) or isinstance(f, atoms.Increase) or isinstance(f, atoms.Equal) class Atom(Formula, Condition, Effect):

    def __init__(self, predicate, args):
        """
        Atomic formula (P O1 O2 ... ON).

        :param predicate: the :class:`.Predicate` form of the atom
        :param args: a list of :class:`.Object` arguments to ``predicate``
        :raises ValueError: if ``predicate.types`` does not match the types of ``args``
        .. automethod:: __invert__
        """
        if not all(isinstance(arg, Object) for arg in args):
            raise ValueError(
                'Expected atom arguments %s to extend class Object' % args)
        if len(predicate.types) != len(args):
            raise ValueError('Expected atom arguments %s to have the same length as predicate types %s' % (
                args, predicate.types))
        arg_types = tuple(arg.type for arg in args)
        if not all(ty == arg_ty or ty == OBJECT for ty, arg_ty in zip(predicate.types, arg_types)):
            raise ValueError('Expected atom argument types %s to be the same as predicate types %s' % (
                arg_types, predicate.types))
        self.predicate = predicate
        self.args = tuple(args)
        self._hash = None

    @property
    def name(self): return self.predicate.name

    @property
    def types(self): return self.predicate.types

    @property
    def function(self): return self.predicate

    def get_atoms(self): return {self}

    def get_literals(self): return [[self]]

    def get_formulas(self): return []

    def get_quantified(self): return set()

    def substitute(self, atom, subformula): pass

    def holds(self, atoms, constants): return self in atoms

    def positive_supporters(self, atoms, constants): return {
        self} if self.holds(atoms, constants) else None

    def negative_supporters(self, atoms, constants): return None if self.holds(
        atoms, constants) else {self}

    def add(self, atoms, constants): return [self]

    def delete(self, atoms, constants): return []

    def is_function(self): return isinstance(self.function, Function)

    def get_parameters(self):
        return set(arg for arg in self.args if isinstance(arg, Parameter))

    def propositional(self, constants): return self

    def dequantify(self, constants): return self

    def instantiate(self, parameter_map):
        return Atom(self.predicate, [parameter_map[arg] if arg in parameter_map else arg for arg in self.args])

    def de_morgan(self, sign=True):
        return self if sign else stripstream.pddl.logic.connectives.Not(self)

    def __invert__(self):
        """
        Negates the atom.

        :return: :class:`.Not` of the atom
        """
        return stripstream.pddl.logic.connectives.Not(self)

    def __eq__(self, other):
        return type(self) == type(other) and self.predicate == other.predicate and self.args == other.args

    def __ne__(self, other):
        return not(self == other)

    def __hash__(self):
        if self._hash is None:
            self._hash = hash((self.__class__, self.predicate, self.args))
        return self._hash

    def __repr__(self):
        return self.predicate.name + '(' + ','.join(repr(arg) for arg in self.args) + ')'

    def pddl(self):
        return '(' + ' '.join([self.name] + [arg.pddl() for arg in self.args]) + ')'

    def typed_pddl(self):
        return '(' + self.name + ''.join(' ' + arg.typed_pddl() for arg in self.args) + ')' class Cost(Increase):
    """
    Increase cost atom (increase total-cost F) which extends :class:`.Function` by
    setting ``function`` to be :class:`.TotalCost()`.
    """

    def __init__(self, cost):
        """
        :param cost: the numeric or :class:`.Function` amount that :class:`.TotalCost()` increases
        """
        super(Cost, self).__init__(TotalCost(), cost) class Exists(Quantifier, Condition):
    """
    Existential quantifier (exists (X1 X2 ... XN) P).

    Example collision free for some pose formula:

    .. code:: python

      BLOCK, POSE = EasyType(), EasyType()
      AtPose = EasyPredicate(BLOCK, POSE)
      CollisionFree = EasyPredicate(POSE, POSE)
      B2 = EasyParameter(BLOCK)
      P1, P2 = EasyParameter(POSE), EasyParameter(POSE)

      f = Exists([P2], And(AtPose(B2, P2), CollisionFree(P1, P2)))
    """
    _pddl_name = 'exists'
    _Connective = Or

    def de_morgan(self, sign=True):
        formula = self.formula.de_morgan(sign=sign)
        return Exists(self.args, formula) if sign else ForAll(self.args, formula)

    def dequantify(self, constants):
        return self.formula.dequantify(constants)

    def holds(self, atoms, constants):
        return any(formula.holds(atoms, constants) for formula in self.expand(constants))

    def positive_supporters(self, atoms, constants):
        for formula in self.expand(constants):
            child_literals = formula.positive_supporters(atoms, constants)
            if child_literals is not None:
                return child_literals
        return None

    def negative_supporters(self, atoms, constants):
        literals = set()
        for formula in self.expand(constants):
            child_literals = formula.negative_supporters(atoms, constants)
            if child_literals is None:
                return None
            literals |= child_literals
        return literals class And(Connective, Condition, Effect):
    """
    Logical conjunction (and P1 P2 ... PN).

    .. code:: python

      BLOCK, POSE = EasyType(), EasyType()
      AtPose = EasyPredicate(BLOCK, POSE)
      CollisionFree = EasyPredicate(POSE, POSE)
      B2 = EasyParameter(BLOCK)
      P1, P2 = EasyParameter(POSE), EasyParameter(POSE)

      f = And(AtPose(B2, P2), CollisionFree(P1, P2))
    """
    _pddl_name = 'and'

    def de_morgan(self, sign=True):
        formulas = [f.de_morgan(sign=sign) for f in self.formulas]

        return And(*formulas) if sign else Or(*formulas)

    def get_literals(self):
        dnfs = [f.get_literals() for f in self.formulas]
        for combo in product(*dnfs):

            yield reduce(operator.add, combo)

    def holds(self, atoms, constants):
        return all(formula.holds(atoms, constants) for formula in self.formulas)

    def positive_supporters(self, atoms, constants):
        literals = set()
        for formula in self.formulas:
            child_literals = formula.positive_supporters(atoms, constants)
            if child_literals is None:
                return None
            literals |= child_literals
        return literals

    def negative_supporters(self, atoms, constants):
        for formula in self.formulas:
            child_literals = formula.negative_supporters(atoms, constants)
            if child_literals is not None:
                return child_literals
        return None

    def add(self, atoms, constants):
        return flatten(formula.add(atoms, constants) for formula in self.formulas)

    def delete(self, atoms, constants):
        return flatten(formula.delete(atoms, constants) for formula in self.formulas) class Not(Connective, Condition, Effect):
    """
    Logical negation (not P).

    .. code:: python

      CONF = EasyType()
      AtConf = EasyPredicate(CONF, CONF)
      Q1 = EasyParameter(CONF)

      f = Not(AtConf(Q1)))
    """
    _pddl_name = 'not'

    def __init__(self, formula):
        """
        :param formula: a :class:`.Formula`
        """
        super(Not, self).__init__(formula)

    @property
    def formula(self): return self.formulas[0]

    def de_morgan(self, sign=True): return self.formula.de_morgan(
        sign=not sign)

    def simplify(self): return self

    def get_literals(self):
        dnf = self.formulas[0].get_literals()
        for combo in product(*dnf):
            yield map(utils.invert, combo)

    def holds(self, atoms, constants):
        return not self.formula.holds(atoms, constants)

    def positive_supporters(self, atoms, constants):
        return set(map(utils.invert, self.formula.negative_supporters(atoms, constants)))

    def negative_supporters(self, atoms, constants):
        return set(map(utils.invert, self.formula.positive_supporters(atoms, constants)))

    def add(self, atoms, constants):
        return self.formula.delete(atoms, constants)

    def delete(self, atoms, constants):
        return self.formula.add(atoms, constants)

    def __eq__(self, other):
        return type(self) == type(other) and self.formula == other.formula

    def __ne__(self, other):
        return not(self == other)

    def __hash__(self):
        return hash((self.__class__, self.formulas)) class Constant(Object):
    pass
    """
  PDDL constant which extends :class:`.Object`.
  """ class Parameter(Object):

    def __init__(self, name, type):
        """
        PDDL parameter which extends :class:`.Object`.

        :param name: the string name of the parameter
        :param type: the :class:`.Type` of the parameter
        """
        super(Parameter, self).__init__(PARAM_PREFIX + name, type)

    @property
    def original(self):
        return self.name[len(PARAM_PREFIX):] def get_param_names(literals):
    param_names = {}
    for literal in literals:
        assert is_literal(literal)
        atom = literal.formula if isinstance(literal, Not) else literal
        for arg in atom.args:
            if isinstance(arg, Parameter):
                if arg.name in param_names:
                    assert param_names[arg.name] == arg
                else:
                    param_names[arg.name] = arg
    return param_names def flatten(iterable_of_iterables):
    return (item for iterables in iterable_of_iterables for item in iterables)
from stripstream.pddl.logic.utils import is_literal
from stripstream.pddl.logic.atoms import Atom, Cost
from stripstream.pddl.logic.quantifiers import Exists
from stripstream.pddl.logic.connectives import And, Not
from stripstream.pddl.objects import Constant, Parameter
from stripstream.pddl.utils import get_param_names
from stripstream.utils import flatten

      a = Action(name='move', parameters=[Q1, Q2],
                 condition=AtConf(Q1),
                 effect=And(AtConf(Q2), Not(AtConf(Q1))))
    """

    def __init__(self, name, parameters, condition, effect, cost=None):
        """
        :param name: the name of the action
        :param parameters: a list of :class:`.Parameter`
        :param condition: a :class:`.Condition`
        :param effect: an :class:`.Effect`
        :param cost: a numeric cost
        """
        super(Action, self).__init__(parameters, condition, effect)
        self.name = name.lower()
        self.cost_included = False
        self.cost = cost

    def instantiate(self, args): return ActionInstance(self, args)

    def pddl(self, costs):
        s = '(:action ' + self.name + '\n'
        s += '\t:parameters (' + ' '.join(param.typed_pddl()
                                          for param in self.parameters) + ')\n'
        s += '\t:precondition ' + self.condition.pddl() + '\n'
        augmented_effect = self.effect
        if costs and self.cost is not None and not self.cost_included:
            augmented_effect = And(augmented_effect, Cost(self.cost))
        s += '\t:effect ' + augmented_effect.pddl()
        return s + ')'

    def to_strips(self, constants):
        condition = self.condition.dequantify(constants)
        effect = self.effect.dequantify(constants)
        all_parameters = condition.get_parameters() | effect.get_parameters()
        parameters = list(self.parameters) + \
            [p for p in all_parameters if p not in self.parameters]
        [effs] = effect.get_literals()
        for i, cons in enumerate(condition.get_literals()):
            instance = STRIPSAction('%s_%s' % (
                self.name, i), parameters, cons, effs)
            instance.original = self
            yield instance

    def clone(self):
        return Action(self.name[:], self.parameters[:], self.condition.clone(), self.effect.clone())

    def __repr__(self):
        return self.name
    __str__ = __repr__


class STRIPSAction(Action, STRIPS):
    """
    STRIPS action class which extends :class:`.Action` by assuming
    ``condition`` and ``effect`` are conjunctions of literals.

    .. code:: python

      CONF = EasyType()
      AtConf = EasyPredicate(CONF, CONF)
      Q1, Q1 = EasyParameter(CONF), EasyParameter(CONF)

      a = Action(name='move', parameters=[Q1, Q2],
                 conditions=[AtConf(Q1)],
                 effects=[AtConf(Q2), Not(AtConf(Q1))])
    """

    def __init__(self, name, parameters, conditions, effects, cost=None):
        """
        :param name: the string name of the action
        :param parameters: a list of :class:`.Parameter`
        :param conditions: a list of :class:`.Atom` or :class:`.Not`
        :param effects: a list of :class:`.Atom` or :class:`.Not`
        :param cost: a numeric cost
        """
        if not all(is_literal(c) for c in conditions):
            raise ValueError(
                'Conditions must all be literals: %s' % conditions)
        if not all(is_literal(e) for e in effects):
            raise ValueError('Effects must all be literals: %s' % effects)
        self.conditions, self.effects = conditions, effects
        super(STRIPSAction, self).__init__(name, parameters,
                                           And(*conditions), And(*effects), cost=cost)

    def add_effects(self, *new_effects):
        self.effects += new_effects

        super(STRIPSAction, self).add_effects(*new_effects)

    def add_list(self):
        return filter(lambda a: isinstance(a, Atom), self.effects)

    def delete_list(self):
        return [a.formula for a in self.effects if isinstance(a, Not)]


class Axiom(Operator):
    """
    PDDL axiom (derived predicate).

    .. code:: python

      BLOCK, POSE = EasyType(), EasyType()
      AtPose = EasyPredicate(BLOCK, POSE)
      CollisionFree = EasyPredicate(POSE, POSE)
      Safe = EasyPredicate(BLOCK, POSE)
      B2 = EasyParameter(BLOCK)
      P1, P2 = EasyParameter(POSE), EasyParameter(POSE)

      x = Axiom(effect=Safe(B2, P1),
                condition=Exists([P2], And(AtPose(B2, P2), CollisionFree(P1, P2))))
    """

    def __init__(self, effect, condition):
        """
        :param effect: the :class:`.Atom` derived predicate
        :param condition: a :class:`.Condition`
        """

        if not isinstance(effect, Atom):