Exemple #1
0
def translate_strips_operators(actions, strips_to_sas, ranges, mutex_dict,
                            mutex_ranges, implied_facts):
    # FOND Step 1: Group actions by name. Different actions with the same name
    #         are in fact just different non-deterministic versions of the
    #         same action with equal parameters and conditions.
    actions_by_name = partition(actions, (lambda a: a.name))
    result = []
    for action_list in actions_by_name:
        sas_ops = translate_strips_operator(action_list, strips_to_sas, ranges,
                                            mutex_dict, mutex_ranges,
                                            implied_facts)
        if len(sas_ops) > 0:
            # FOND Step 2: Translate groups of actions with the same name 
            # and precondition together.
            operatorname = sas_ops[0].name
            assert all(op.name == operatorname for op in sas_ops)
            operatorcost = sas_ops[0].cost
            assert all(op.cost == operatorcost for op in sas_ops)
            observation = sas_ops[0].observation
            assert all(op.observation == observation for op in sas_ops)
            #preconditions = sas_ops[0].get_preconditions()
            ops_by_name = partition(sas_ops, (lambda a: repr(a.get_preconditions())))
            for op_list in ops_by_name:
                prevail = set()
                pre_post = []
                for op in op_list:
                    prevail |= set(op.prevail)
                    pre_post.append(sorted(op.pre_post))
                result.append(sas_tasks.SASOperator(operatorname, prevail, pre_post, operatorcost, observation))
        
    return result
Exemple #2
0
    def translate_operator(self, op):
        """Compute a new operator from op where the var/value renaming has
        been applied. Return None if op should be pruned (because it
        is always inapplicable or has no effect.)"""

        # We do not call this apply_to_operator, breaking the analogy
        # with the other methods, because it creates a new operator
        # rather than transforming in-place. The reason for this is
        # that it would be quite difficult to generate the operator
        # in-place.

        # This method is trickier than it may at first appear. For
        # example, pre_post values should be fully sorted (see
        # documentation in the sas_tasks module), and pruning effect
        # conditions from a conditional effects can break this sort
        # order. Recreating the operator from scratch solves this
        # because the pre_post entries are sorted by
        # SASOperator.__init__.

        # Also, when we detect a pre_post pair where the effect part
        # can never trigger, the precondition part is still important,
        # but may be demoted to a prevail condition. Whether or not
        # this happens depends on the presence of other pre_post
        # entries for the same variable. We solve this by computing
        # the sorting into prevail vs. preconditions from scratch, too.

        applicability_conditions = op.get_applicability_conditions()
        try:
            self.convert_pairs(applicability_conditions)
        except Impossible:
            # The operator is never applicable.
            return None
        conditions_dict = dict(applicability_conditions)
        new_prevail_vars = set(conditions_dict)
        new_pre_post = []
        new_assign_effect = []
        for entry in op.pre_post:
            new_entry = self.translate_pre_post(entry, conditions_dict)
            if new_entry is not None:
                new_pre_post.append(new_entry)
                # Mark the variable in the entry as not prevailed.
                new_var = new_entry[0]
                new_prevail_vars.discard(new_var)

        for entry in op.assign_effects:
            new_entry = self.translate_assign_effect(entry)
            if new_entry is not None:
                new_assign_effect.append(new_entry)

        if not new_pre_post and not new_assign_effect:
            # The operator has no effect.
            return None
        new_prevail = sorted((var, value)
                             for (var, value) in conditions_dict.items()
                             if var in new_prevail_vars)
        return sas_tasks.SASOperator(name=op.name,
                                     prevail=new_prevail,
                                     pre_post=new_pre_post,
                                     assign_effect=new_assign_effect,
                                     cost=op.cost)
Exemple #3
0
def build_sas_operator(name, condition, effects_by_variable, cost, ranges,
                       implied_facts):
    if False:  #options.add_implied_preconditions:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])
    prevail_and_pre = dict(condition)
    pre_post = []
    for var, effects_on_var in effects_by_variable.items():
        orig_pre = condition.get(var, -1)
        added_effect = False
        for post, eff_conditions in effects_on_var.items():
            pre = orig_pre
            # if the effect does not change the variable value, we ignore it
            if pre == post:
                continue
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            if ranges[var] == 2:
                # Apply simplifications for binary variables.
                if prune_stupid_effect_conditions(var, post,
                                                  eff_condition_lists,
                                                  effects_on_var):
                    global simplified_effect_condition_counter
                    simplified_effect_condition_counter += 1
                # if (options.add_implied_preconditions and pre == -1 and
                #         (var, 1 - post) in implied_precondition):
                if False:
                    global added_implied_precondition_counter
                    added_implied_precondition_counter += 1
                    pre = 1 - post
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                # and we do not want to keep an effect whose condition contradicts
                # a pre- or prevail condition
                filtered_eff_condition = []
                eff_condition_contradicts_precondition = False
                for variable, value in eff_condition:
                    if variable in prevail_and_pre:
                        if prevail_and_pre[variable] != value:
                            eff_condition_contradicts_precondition = True
                            break
                    else:
                        filtered_eff_condition.append((variable, value))
                if eff_condition_contradicts_precondition:
                    continue
                pre_post.append((var, pre, post, filtered_eff_condition))
                added_effect = True
        if added_effect:
            # the condition on var is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            condition.pop(var, -1)
    if not pre_post:  # operator is noop
        return None
    prevail = list(condition.items())
    return sas_tasks.SASOperator(name, prevail, pre_post, cost)
Exemple #4
0
def build_sas_operator(name, condition, effects_by_variable, cost, ranges,
                       implied_facts):
    if ADD_IMPLIED_PRECONDITIONS:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])

    pre_post = []
    for var in effects_by_variable:
        orig_pre = condition.get(var, -1)
        none_of_those = ranges[var] - 1
        has_delete_effect = none_of_those in effects_by_variable[var]
        for post, eff_conditions in effects_by_variable[var].items():
            pre = orig_pre
            # if the effect does not change the variable value, we ignore it
            # unless there is a delete effect present, in which case the add
            # effect can overwrite the delete effect and it is not safe to
            # ignore it.
            if pre == post and not has_delete_effect:
                continue
            # otherwise the condition on var is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            condition.pop(var, -1)
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            if ranges[var] == 2:
                # Apply simplifications for binary variables.
                if prune_stupid_effect_conditions(var, post,
                                                  eff_condition_lists):
                    global simplified_effect_condition_counter
                    simplified_effect_condition_counter += 1
                if (ADD_IMPLIED_PRECONDITIONS and pre == -1
                        and (var, 1 - post) in implied_precondition):
                    global added_implied_precondition_counter
                    added_implied_precondition_counter += 1
                    pre = 1 - post
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                if (var, pre) in eff_condition:
                    eff_condition.remove((var, pre))
                eff_condition.sort()
                pre_post.append((var, pre, post, eff_condition))
    if not pre_post:  # operator is noop
        return None
    prevail = list(condition.items())
    # We sort effects on a variable in decreasing order of their post
    # condition so that none-of-those effects (corresponding to delete
    # effects) are executed first and possibly overwritten by other
    # effects (add-after-delete semantics).
    pre_post = sorted(pre_post,
                      key=lambda (var, pre, val, cond): (var, -val, cond))
    return sas_tasks.SASOperator(name, prevail, pre_post, cost)
Exemple #5
0
def translate_strips_operator(operator, dictionary, mod_effects_dict, ranges,
                              comp_axioms):
    # NOTE: This function does not really deal with the intricacies of properly
    # encoding delete effects for grouped propositions in the presence of
    # conditional effects. It should work ok but will bail out in more
    # complicated cases even though a conflict does not necessarily exist.

    assert False, "Actions not supported, use durative actions"
    condition = translate_strips_conditions(operator.condition, dictionary,
                                            ranges, comp_axioms)
    if condition is None:
        return None

    effect, possible_add_conflict, mod_eff = translate_add_effects(
        operator.add_effects, dictionary, mod_effects_dict, ranges,
        comp_axioms, False)
    translate_del_effects(operator.del_effects, dictionary, ranges, effect,
                          condition, comp_axioms, False, None)

    if possible_add_conflict:
        print operator.name
    assert not possible_add_conflict, "Conflicting add effects?"

    assign_effect, possible_assign_conflict = \
        translate_assignment_effects(operator.assign_effects, dictionary, ranges, comp_axioms, False)

    if possible_assign_conflict:
        print operator.name
    assert not possible_assign_conflict, "Conflicting assign effects?"

    pre_post = []
    for var in effect:
        for (post, eff_condition_lists) in effect[var].iteritems():
            pre = condition.get(var, -1)
            if pre != -1:
                del condition[var]
            for eff_condition in eff_condition_lists:
                pre_post.append((var, pre, post, eff_condition))
    prevail = condition.items()

    assign_effects = []
    for var in assign_effect:
        for ((op, valvar),
             eff_condition_lists) in assign_effect[var].iteritems():
            for eff_condition in eff_condition_lists:
                sas_effect = sas_tasks.SASAssignmentEffect(
                    var, op, valvar, eff_condition)
                assign_effects.append(sas_effect)

    return sas_tasks.SASOperator(operator.name, prevail, pre_post,
                                 assign_effects)
Exemple #6
0
def build_sas_operator(name, condition, effects_by_variable, cost, ranges,
                       implied_facts, observation):
    if ADD_IMPLIED_PRECONDITIONS:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])

    pre_post = []
    for var in effects_by_variable:
        orig_pre = condition.get(var, -1)
        for post, eff_conditions in effects_by_variable[var].items():
            pre = orig_pre
            # if the effect does not change the variable value, we ignore it
            if pre == post:
                continue
            # otherwise the condition on var is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            condition.pop(var, -1)
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            if ranges[var] == 2:
                # Apply simplifications for binary variables.
                if prune_stupid_effect_conditions(var, post,
                                                  eff_condition_lists):
                    global simplified_effect_condition_counter
                    simplified_effect_condition_counter += 1
                if (ADD_IMPLIED_PRECONDITIONS and pre == -1
                        and (var, 1 - post) in implied_precondition):
                    global added_implied_precondition_counter
                    added_implied_precondition_counter += 1
                    pre = 1 - post
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                if (var, pre) in eff_condition:
                    eff_condition.remove((var, pre))
                pre_post.append((var, pre, post, eff_condition))

    # FOND: Noops as nondeterminstic effects are allowed!
    #if not pre_post:  # operator is noop
    #return None
    prevail = list(condition.items())
    return sas_tasks.SASOperator(name, prevail, pre_post, cost, observation)
Exemple #7
0
def translate_strips_operator(operator, dictionary, ranges):
    # NOTE: This function does not really deal with the intricacies of properly
    # encoding delete effects for grouped propositions in the presence of
    # conditional effects. It should work ok but will bail out in more
    # complicated cases even though a conflict does not necessarily exist.

    possible_add_conflict = False

    condition = translate_strips_conditions(operator.precondition, dictionary,
                                            ranges)
    if condition is None:
        return None

    effect = {}

    for conditions, fact in operator.add_effects:
        eff_condition = translate_strips_conditions(conditions, dictionary,
                                                    ranges)
        if eff_condition is None:  # Impossible condition for this effect.
            continue
        eff_condition = eff_condition.items()
        for var, val in dictionary[fact]:
            effect_pair = effect.get(var)
            if not effect_pair:
                effect[var] = (val, [eff_condition])
            else:
                other_val, eff_conditions = effect_pair
                # Don't flag conflict just yet... the operator might be invalid
                # because of conflicting add/delete effects (see pipesworld).
                if other_val != val:
                    possible_add_conflict = True
                eff_conditions.append(eff_condition)

    for conditions, fact in operator.del_effects:
        eff_condition_dict = translate_strips_conditions(
            conditions, dictionary, ranges)
        if eff_condition_dict is None:
            continue
        eff_condition = eff_condition_dict.items()
        for var, val in dictionary[fact]:
            none_of_those = ranges[var] - 1

            other_val, eff_conditions = effect.setdefault(
                var, (none_of_those, []))

            if other_val != none_of_those:
                # Look for matching add effect; ignore this del effect if found.
                assert eff_condition in eff_conditions or [] in eff_conditions, \
                              "Add effect with uncertain del effect partner?"
                if other_val == val:
                    if ALLOW_CONFLICTING_EFFECTS:
                        # Conflicting ADD and DEL effect. This is *only* allowed if
                        # this is also a precondition, in which case there is *no*
                        # effect (the ADD takes precedence). We delete the add effect here.
                        if condition.get(var) != val:
                            # HACK HACK HACK!
                            # There used to be an assertion here that actually
                            # forbid this, but this was wrong in Pipesworld-notankage
                            # (e.g. task 01). The thing is, it *is* possible for
                            # an operator with proven (with the given invariants)
                            # inconsistent preconditions to actually end up here if
                            # the inconsistency of the preconditions is not obvious at
                            # the SAS+ encoding level.
                            #
                            # Example: Pipes-Notank-01, operator
                            # (pop-unitarypipe s12 b4 a1 a2 b4 lco lco).
                            # This has precondition last(b4, s12) and on(b4, a2) which
                            # is inconsistent because b4 can only be in one place.
                            # However, the chosen encoding encodes *what is last in s12*
                            # separately, and so the precondition translates to
                            # "last(s12) = b4 and on(b4) = a2", which does not look
                            # inconsistent at first glance.
                            #
                            # Something reasonable to do here would be to make a
                            # decent check that the precondition is indeed inconsistent
                            # (using *all* mutexes), but that seems tough with this
                            # convoluted code, so we just warn and reject the operator.
                            print "Warning: %s rejected. Cross your fingers." % (
                                operator.name)
                            return None
                            assert False

                        assert eff_conditions == [[]]
                        del effect[var]
                    else:
                        assert not eff_condition and not eff_conditions[
                            0], "Uncertain conflict"
                        return None  # Definite conflict otherwise.
            else:
                if condition.get(var) != val and eff_condition_dict.get(
                        var) != val:
                    if var in condition:
                        ## HACK HACK HACK! There is a precondition on the variable for
                        ## this delete effect on another value, so there is no need to
                        ## represent the delete effect. Right? Right???
                        continue
                    # Need a guard for this delete effect.
                    assert var not in condition and var not in eff_condition, "Oops?"
                    eff_condition.append((var, val))
                eff_conditions.append(eff_condition)

    if possible_add_conflict:
        print operator.name

    assert not possible_add_conflict, "Conflicting add effects?"

    # assert eff_condition != other_condition, "Duplicate effect"
    # assert eff_condition and other_condition, "Dominated conditional effect"

    pre_post = []
    for var, (post, eff_condition_lists) in effect.iteritems():
        pre = condition.get(var, -1)
        if pre != -1:
            del condition[var]
        for eff_condition in eff_condition_lists:
            pre_post.append((var, pre, post, eff_condition))
    prevail = condition.items()

    return sas_tasks.SASOperator(operator.name, prevail, pre_post,
                                 operator.cost)
Exemple #8
0
def translate_strips_operator_aux(operator, dictionary, ranges, mutex_dict,
                                  mutex_ranges, implied_facts, condition):
    # NOTE: This function does not really deal with the intricacies of properly
    # encoding delete effects for grouped propositions in the presence of
    # conditional effects. It should work ok but will bail out in more
    # complicated cases even though a conflict does not necessarily exist.
    possible_add_conflict = False

    effect = {}

    for conditions, fact in operator.add_effects:
        eff_condition_list = translate_strips_conditions(
            conditions, dictionary, ranges, mutex_dict, mutex_ranges)
        if eff_condition_list is None:  # Impossible condition for this effect.
            continue
        eff_condition = [
            list(eff_cond.items()) for eff_cond in eff_condition_list
        ]
        for var, val in dictionary[fact]:
            if condition.get(var) == val:
                # Effect implied by precondition.
                global removed_implied_effect_counter
                removed_implied_effect_counter += 1
                # print "Skipping effect of %s..." % operator.name
                continue
            effect_pair = effect.get(var)
            if not effect_pair:
                effect[var] = (val, eff_condition)
            else:
                other_val, eff_conditions = effect_pair
                # Don't flag conflict just yet... the operator might be invalid
                # because of conflicting add/delete effects (see pipesworld).
                if other_val != val:
                    possible_add_conflict = True
                eff_conditions.extend(eff_condition)

    for conditions, fact in operator.del_effects:
        eff_condition_list = translate_strips_conditions(
            conditions, dictionary, ranges, mutex_dict, mutex_ranges)
        if eff_condition_list is None:
            continue
        eff_condition = [
            list(eff_cond.items()) for eff_cond in eff_condition_list
        ]
        for var, val in dictionary[fact]:
            none_of_those = ranges[var] - 1

            other_val, eff_conditions = effect.setdefault(
                var, (none_of_those, []))

            if other_val != none_of_those:
                # Look for matching add effect; ignore this del effect if found.
                for cond in eff_condition:
                    if cond not in eff_conditions and [] not in eff_conditions:
                        print("Condition:")
                        print(cond)
                        print("Operator:")
                        operator.dump()
                        assert False, "Add effect with uncertain del effect partner?"
                if other_val == val:
                    if ALLOW_CONFLICTING_EFFECTS:
                        # Conflicting ADD and DEL effect. This is *only* allowed if
                        # this is also a precondition, in which case there is *no*
                        # effect (the ADD takes precedence). We delete the add effect here.
                        if condition.get(var) != val:
                            # HACK HACK HACK!
                            # There used to be an assertion here that actually
                            # forbid this, but this was wrong in Pipesworld-notankage
                            # (e.g. task 01). The thing is, it *is* possible for
                            # an operator with proven (with the given invariants)
                            # inconsistent preconditions to actually end up here if
                            # the inconsistency of the preconditions is not obvious at
                            # the SAS+ encoding level.
                            #
                            # Example: Pipes-Notank-01, operator
                            # (pop-unitarypipe s12 b4 a1 a2 b4 lco lco).
                            # This has precondition last(b4, s12) and on(b4, a2) which
                            # is inconsistent because b4 can only be in one place.
                            # However, the chosen encoding encodes *what is last in s12*
                            # separately, and so the precondition translates to
                            # "last(s12) = b4 and on(b4) = a2", which does not look
                            # inconsistent at first glance.
                            #
                            # Something reasonable to do here would be to make a
                            # decent check that the precondition is indeed inconsistent
                            # (using *all* mutexes), but that seems tough with this
                            # convoluted code, so we just warn and reject the operator.
                            print("Warning: %s rejected. Cross your fingers." %
                                  (operator.name))
                            if DEBUG:
                                operator.dump()
                            return None
                            assert False

                        assert eff_conditions == [[]]
                        del effect[var]
                    else:
                        assert not eff_condition[0] and not eff_conditions[
                            0], "Uncertain conflict"
                        return None  # Definite conflict otherwise.
            else:  # no add effect on this variable
                if condition.get(var) != val:
                    if var in condition:
                        ## HACK HACK HACK! There is a precondition on the variable for
                        ## this delete effect on another value, so there is no need to
                        ## represent the delete effect. Right? Right???
                        del effect[var]
                        continue
                    for index, cond in enumerate(eff_condition_list):
                        if cond.get(var) != val:
                            # Need a guard for this delete effect.
                            assert (var not in condition and var
                                    not in eff_condition[index]), "Oops?"
                            eff_condition[index].append((var, val))
                eff_conditions.extend(eff_condition)

    if possible_add_conflict:
        operator.dump()

    assert not possible_add_conflict, "Conflicting add effects?"

    # assert eff_condition != other_condition, "Duplicate effect"
    # assert eff_condition and other_condition, "Dominated conditional effect"

    if ADD_IMPLIED_PRECONDITIONS:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])

    pre_post = []
    for var, (post, eff_condition_lists) in effect.items():
        pre = condition.pop(var, -1)
        if ranges[var] == 2:
            # Apply simplifications for binary variables.
            if prune_stupid_effect_conditions(var, post, eff_condition_lists):
                global simplified_effect_condition_counter
                simplified_effect_condition_counter += 1
            if (ADD_IMPLIED_PRECONDITIONS and pre == -1
                    and (var, 1 - post) in implied_precondition):
                global added_implied_precondition_counter
                added_implied_precondition_counter += 1
                pre = 1 - post
                # print "Added precondition (%d = %d) to %s" % (
                #     var, pre, operator.name)
        for eff_condition in eff_condition_lists:
            pre_post.append((var, pre, post, eff_condition))
    prevail = list(condition.items())

    return sas_tasks.SASOperator(operator.name, prevail, pre_post,
                                 operator.cost)
Exemple #9
0
def op_from_comm(d):
    op = sas_tasks.SASOperator(d[0], d[1], d[2], d[3])
    op.owner = d[4]
    op.global_id = d[5]
    return op
Exemple #10
0
def build_sas_operator(name, condition, effects_by_variable, cost, ranges,
                       implied_facts):
    if options.add_implied_preconditions:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])
    prevail_and_pre = dict(condition)
    pre_post = []
    for var, effects_on_var in effects_by_variable.items():
        orig_pre = condition.get(var, -1)
        none_of_those = ranges[var] - 1
        has_delete_effect = none_of_those in effects_by_variable[var]
        added_effect = False
        for post, eff_conditions in effects_on_var.items():
            pre = orig_pre
            # If the effect does not change the variable value, we ignore it if
            # we are enforcing definite effects or there is no delete effect
            # present. Under add-after-delete semantics for conditional effects
            # add effects can overwrite delete effects and it is not safe to
            # ignore add effects.
            if pre == post and (options.enforce_definite_effects
                                or not has_delete_effect):
                continue
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            if ranges[var] == 2:
                # Apply simplifications for binary variables.
                if prune_stupid_effect_conditions(var, post,
                                                  eff_condition_lists,
                                                  effects_on_var):
                    global simplified_effect_condition_counter
                    simplified_effect_condition_counter += 1
                if (options.add_implied_preconditions and pre == -1
                        and (var, 1 - post) in implied_precondition):
                    global added_implied_precondition_counter
                    added_implied_precondition_counter += 1
                    pre = 1 - post
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                # and we do not want to keep an effect whose condition contradicts
                # a pre- or prevail condition
                filtered_eff_condition = []
                eff_condition_contradicts_precondition = False
                for variable, value in eff_condition:
                    if variable in prevail_and_pre:
                        if prevail_and_pre[variable] != value:
                            eff_condition_contradicts_precondition = True
                            break
                    else:
                        filtered_eff_condition.append((variable, value))
                if eff_condition_contradicts_precondition:
                    continue
                filtered_eff_condition.sort()  # TODO: Is it already sorted?
                pre_post.append((var, pre, post, filtered_eff_condition))
                added_effect = True
        if added_effect:
            # the condition on var is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            condition.pop(var, -1)
    if not pre_post:  # operator is noop
        return None
    prevail = list(condition.items())
    return sas_tasks.SASOperator(name, prevail, pre_post, cost)
Exemple #11
0
def build_sas_operator(name, condition, effects_by_variable,
                       ass_effects_by_variable, deprecated_cost, ranges,
                       implied_facts, relevant_numeric_variables):
    #    print("build_sas_operator with relevant vars:", relevant_numeric_variables)
    #    if DEBUG: print("Building SAS Operator %s with %d logic and %d numeric effects" % (name, len(effects_by_variable),len(ass_effects_by_variable)))
    if options.add_implied_preconditions:
        implied_precondition = set()
        for fact in condition.items():
            implied_precondition.update(implied_facts[fact])
    prevail_and_pre = dict(condition)
    pre_post = []
    num_pre_post = []
    for var in effects_by_variable:
        orig_pre = condition.get(var, -1)
        added_effect = False
        for post, eff_conditions in effects_by_variable[var].items():
            pre = orig_pre
            # if the effect does not change the variable value, we ignore it
            if pre == post:
                continue
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            if ranges[var] == 2:
                # Apply simplifications for binary variables.
                if prune_stupid_effect_conditions(var, post,
                                                  eff_condition_lists):
                    global simplified_effect_condition_counter
                    simplified_effect_condition_counter += 1
                if (options.add_implied_preconditions and pre == -1
                        and (var, 1 - post) in implied_precondition):
                    global added_implied_precondition_counter
                    added_implied_precondition_counter += 1
                    pre = 1 - post
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                # and we do not want to keep an effect whose condition contradicts
                # a pre- or prevail condition
                filtered_eff_condition = []
                eff_condition_contradicts_precondition = False
                for variable, value in eff_condition:
                    if variable in prevail_and_pre:
                        if prevail_and_pre[variable] != value:
                            eff_condition_contradicts_precondition = True
                            break
                    else:
                        filtered_eff_condition.append((variable, value))
                if eff_condition_contradicts_precondition:
                    continue
                pre_post.append((var, pre, post, filtered_eff_condition))
                added_effect = True
        if added_effect:
            # the condition on var is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            condition.pop(var, -1)

    for numvar in ass_effects_by_variable:
        #        if DEBUG: print("Numeric effect on numvar = %s" % numvar)
        #        orig_pre = condition.get(numvar, -1)
        #        assert orig_pre == -1 # numeric variables cannot occur in preconditions (instead propositional variables are derived by axioms)
        #    numeric variables and logic variables are not stored in the same data structures, but they *do* can have the same index within their
        #    respective arrays
        for (ass_op, post_var
             ), eff_conditions in ass_effects_by_variable[numvar].items():
            #            if DEBUG: print("assignment operator : >%s<" % ass_op)
            #            if DEBUG: print("post condition variable : %d" % post_var)
            # otherwise the condition on numvar is not a prevail condition but a
            # precondition, so we remove it from the prevail condition
            eff_condition_lists = [
                sorted(eff_cond.items()) for eff_cond in eff_conditions
            ]
            for eff_condition in eff_condition_lists:
                # we do not need to represent a precondition as effect condition
                num_pre_post.append((numvar, ass_op, post_var, eff_condition))


#    if DEBUG: print("pre_post = %s " % pre_post)
#    if DEBUG: print("num_pre_post = %s " % num_pre_post)
    if not pre_post:  # build only operators with at least one regular effect (not instrumentation)
        irrelevant = True
        for effect in num_pre_post:
            #            print ("numeric effect ", effect)
            if effect[0] in relevant_numeric_variables:
                #                print("RELEVANT! because ", effect[0], " is in ", relevant_numeric_variables)
                irrelevant = False
                break
        if irrelevant:
            #            if DEBUG: print("operator is noop - return None")
            return None
    prevail = list(condition.items())
    return sas_tasks.SASOperator(name, prevail, pre_post, num_pre_post,
                                 deprecated_cost)