Пример #1
0
def read_problem_from_file(filename):

    # Auxiliary function to parse a string of the form (prefix + "rest")
    # If string is of this form, it returns True,"rest"
    # Otherwise, it returns False,""
    def remove_prefix_if_possible(line, prefix):
        if line[0:len(prefix)] == prefix:
            return True,line[len(prefix):];
        else:
            return False,"";

    try:
        file = open(filename, "r");
        # Initialize
        initial_str = "";
        goals_str = "";
        t_max_str = "20"; # default value is 20
        actions_list = [];
        # Read the file line by line
        for line in file.readlines():
            stripped_line = line.strip();
            if stripped_line != "" and stripped_line[0] != '#':
                # Lines specifying initial state
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"initial: ");
                if match:
                    initial_str = rest_of_line.strip();
                    if expr(initial_str) == True or expr(initial_str) == None:
                        initial_str = "[]";
                # Lines specifying goals
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"goals: ");
                if match:
                    goals_str = rest_of_line.strip();
                    if expr(goals_str) == True or expr(goals_str) == None:
                        goals_str = "[]";
                # Lines specifying t_max
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"t_max: ");
                if match:
                    t_max_str = rest_of_line.strip();
                # Lines specifying an action
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"action: ");
                if match:
                    action_strs = rest_of_line.split(";");
                    action = Action(action_strs[0], precond=action_strs[1], effect=action_strs[2]);
                    if expr(action.precond) == None or expr(action.precond) == True:
                        action.precond = [];
                    if expr(action.effect) == None or expr(action.effect) == True:
                        action.effect = [];
                    actions_list.append(action);
        # Create planning_problem and t_max from the data stored after reading, and return them
        planning_problem = PlanningProblem(initial=initial_str, goals=goals_str, actions=actions_list);
        t_max = int(t_max_str);
        return planning_problem, t_max;

    # If exception occurs, print error message and return None,None
    except Exception as e:
        print("Something went wrong while reading from " + filename + " (" + str(e) + ")");
        return None, None;
Пример #2
0
 def setUp(self):
     self.p = have_cake()
     self.pg = PlanningGraph(self.p, self.p.initial)
     # some independent nodes for testing mutex
     self.na1 = PgNode_a(Action(expr('Go(here)'),
                                [[], []], [[expr('At(here)')], []]))
     self.na2 = PgNode_a(Action(expr('Go(there)'),
                                [[], []], [[expr('At(there)')], []]))
     self.na3 = PgNode_a(Action(expr('Noop(At(there))'),
                                [[expr('At(there)')], []], [[expr('At(there)')], []]))
     self.na4 = PgNode_a(Action(expr('Noop(At(here))'),
                                [[expr('At(here)')], []], [[expr('At(here)')], []]))
     self.na5 = PgNode_a(Action(expr('Reverse(At(here))'),
                                [[expr('At(here)')], []], [[], [expr('At(here)')]]))
     self.ns1 = PgNode_s(expr('At(here)'), True)
     self.ns2 = PgNode_s(expr('At(there)'), True)
     self.ns3 = PgNode_s(expr('At(here)'), False)
     self.ns4 = PgNode_s(expr('At(there)'), False)
     self.na1.children.add(self.ns1)
     self.ns1.parents.add(self.na1)
     self.na2.children.add(self.ns2)
     self.ns2.parents.add(self.na2)
     self.na1.parents.add(self.ns3)
     self.na2.parents.add(self.ns4)
Пример #3
0
    def test_inconsistent_support_mutex(self):
        self.assertFalse(PlanningGraph.inconsistent_support_mutex(self.pg, self.ns1, self.ns2),
                         "Independent node paths should NOT be inconsistent-support mutex")
        mutexify(self.na1, self.na2)
        self.assertTrue(PlanningGraph.inconsistent_support_mutex(self.pg, self.ns1, self.ns2),
                        "Mutex parent actions should result in inconsistent-support mutex")

        self.na6 = PgNode_a(Action(expr('Go(everywhere)'),
                                   [[], []], [[expr('At(here)'), expr('At(there)')], []]))
        self.na6.children.add(self.ns1)
        self.ns1.parents.add(self.na6)
        self.na6.children.add(self.ns2)
        self.ns2.parents.add(self.na6)
        self.na6.parents.add(self.ns3)
        self.na6.parents.add(self.ns4)
        mutexify(self.na1, self.na6)
        mutexify(self.na2, self.na6)
        self.assertFalse(PlanningGraph.inconsistent_support_mutex(
            self.pg, self.ns1, self.ns2),
            "If one parent action can achieve both states, should NOT be inconsistent-support mutex, even if parent actions are themselves mutex")
def solveEquation(equation, debug=False):
    """
    HOW TO SOLVE EQUATIONS:
    COMBINE
    1.  Convert the input string: x=3+3
    2.  Your initial becomes the following:
        - LeftVar(1)
        - RightConst(3)
        - RightConst(3)
    3.  The actions you generate are the following:
        - CombineRightConst()
            Pre:  RightConst(a) & RightConst(b)
            Post: ~RightConst(a) & ~RightConst(b) & RightConst()
    4.  For the goal, make sure you have the following:
        - BASE_CLAUSE (LeftVar(1) & RightConst(x)) &
        - ~RightConst(3) & ~RightConst(3)
        * Basically, the negation of everything you started with, plus the last line ALWAYS

    ADD
    1.  Convert the following input string: x+3=5
    2.  Your initial becomes the following:
        - LeftVar(1)
        - LeftConst(3)
        - RightConst(5)
    3.  The actions you generate are the following:
        - AddLeftConstant(a)
            Pre:  LeftConst(a)
            Post: ~LeftConst(a) & RightConst(a)
    4.  For the goal, make sure you have the following:
        - BASE_CLAUSE (LeftVar(1) & RightConst(x)) &
        # SUBTRACT RULES
        - ~LeftConst(3)
        # COMBINE RULES
        - ~RightConst(3) & ~RightConst(5) & RightConst(x)

    DIVIDE
    1.  Convert the following input string: 3x-2=6
    2.  Initial Variables:
        - LeftVar(3)
        - LeftConst(-2)
        - RightConst(6)
    3.  The actions you generate are the following:
        - DivideLeftVar(a)
            Pre:  LeftVar(a) & RightConst(x)      This forces it to go after right is finished
            Post: LeftVar(1) & ~LeftVar(a) & DivideLeftVar(a)
    4.  For the goal, don't add anything because a LeftVar(1) is already mandated
    """
    statements, counts = parse_equation(equation)
    # This should be loaded with the state of the equation
    # Ex: LeftVar(3) & LeftConst(-2) & RightConst(6)
    init_string = " & "
    init_string = init_string.join(statements)
    if debug:
        print("\tCounts: " + str(counts))
        print("\tInit: " + init_string)

    # =============== CREATE ALL ACTIONS =====================
    extra_goals = [
    ]  # These are all the extra goals we generate from special cases in actions
    if debug:
        print("\tActions: ")
    actions_arr = []
    for type in ["Const", "Var"]:
        for side in ["Left", "Right"]:
            # The combining method
            side_type = side + type
            name = "Combine" + side_type + "(a, b)"
            precond = side_type + "(a) & " + side_type + "(b)"
            effect = side_type + "(x) & ~" + side_type + "(a) & ~" + side_type + "(b)" + " & " + name
            new_action = Action(name, precond=precond, effect=effect)
            if debug:
                print_action(name, precond, effect)
            actions_arr.append(new_action)

            # Add method
            if side_type == "LeftConst" or side_type == "RightVar":
                # We don't need to add right constants, they are supposed to be on the right
                # Don't need to add left variables, supposed to be on the left
                opposite_side = "Left"
                if side == "Left":
                    opposite_side = "Right"

                name = "Add" + side_type + "(a)"
                precond = side_type + "(a)"
                effect = opposite_side + type + "(a) & ~" + side_type + "(a)" + " & " + name
                if side_type == "RightVar" and len(
                        counts["Var"]["Left"]) == 1 and len(
                            counts["Var"]["Right"]) == 1:
                    # Check if there is a variable on the left, in which case mandate we COMBINE
                    # Ex: 4x=3x+2
                    extra_goals.append(" & ~" + opposite_side + type + "(" +
                                       str(counts["Var"]["Right"][0]) + ")")

                new_action = Action(name, precond=precond, effect=effect)
                if debug:
                    print_action(name, precond, effect)
                actions_arr.append(new_action)

            # Divide method
            """
            5x=x+3

            """
            if side_type == "LeftVar":  # You can only divide VARS once they are on the left
                name = "Divide" + side_type + "(a)"
                precond = side_type + "(a)"
                effect = side_type + "(1) & ~" + side_type + "(a) & " + name  # This is the goal state
                # Special case for 4x=x, x-x=3x, or any scenario when there are no constants
                if counts["Const"]["Right"] + counts["Const"]["Left"] == 0:
                    effect += " & " + "RightConst(x)"

                new_action = Action(name, precond=precond, effect=effect)
                if debug:
                    print_action(name, precond, effect)
                actions_arr.append(new_action)

    # =============== CREATE ALL GOALS =====================

    goals_arr = []  # ALWAYS in the form x=const

    if len(counts["Var"]["Left"]) + len(counts["Var"]["Right"]) > 1:
        goals_arr.append("LeftVar(x)")
    else:
        goals_arr.append("LeftVar(1)")

    if len(counts["Const"]["Left"]) + len(counts["Const"]["Right"]) > 1:
        goals_arr.append("RightConst(x)")
    elif len(counts["Const"]["Left"]) == 1 and len(
            counts["Const"]["Right"]) == 0:
        goals_arr.append("RightConst(" + str(counts["Const"]["Left"][0]) + ")")
    elif len(counts["Const"]["Left"]) == 0 and len(
            counts["Const"]["Right"]) == 1:
        goals_arr.append("RightConst(" + str(counts["Const"]["Right"][0]) +
                         ")")

    found_single_left_var = False
    for statement in statements:
        # ============ COMBINE GOALS ==============
        # We don't want to negate something already in out goal, but we also ONLY want one
        if not found_single_left_var and statement == "LeftVar(1)":
            found_single_left_var = True
            continue  # We would not want to mandate this ONE is negated
        if "RightConst" in statement and len(counts["Const"]["Right"]) == 1:
            continue  # Don't negate it for combination if it's the only one

        goals_arr.append("~" + statement)

        # ============ ADD GOALS ==============
        if "RightVar" in statement:
            goals_arr.append("~" + statement)  # Force to move right var

        elif "LeftConst" in statement:
            goals_arr.append("~" + statement)

        # ============ DIVIDE GOALS ==============

    goal_string = " & "
    goal_string = goal_string.join(goals_arr)

    # ============ DIVIDE GOALS ==============

    # We MUST combine these, not divide them all to one
    if len(counts["Var"]["Left"]) == 1 and len(counts["Var"]["Right"]) == 1:
        extra_goals.append(" & CombineLeftVar(" +
                           str(counts["Var"]["Left"][0]) + ", " +
                           str(counts["Var"]["Right"][0]) + ")")

    if debug:
        print("\tGoals: " + str(goal_string))
        print("\tExtra Goals: " + str(extra_goals))

    for extra_goal in extra_goals:
        goal_string += extra_goal

    equation_plan = PlanningProblem(
        initial=init_string,
        goals=goal_string,
        # Checkout class Action: -> Located in planning.py
        # Line 195 in the Action class has a convert() function, where we can resolve these additions
        actions=actions_arr)
    print("\t==== SOLVING ====")
    action_plan = ForwardPlan(equation_plan)
    steps = []
    plans = astar_search(action_plan)
    if debug:
        print("\tPlans:")
    if plans == None:
        # Check if we actually need to solve the equation, BASE CASE
        if counts["Var"]["Left"] == 1 and counts["Var"][
                "Right"] == 0 and counts["Const"]["Left"] == 0 and counts[
                    "Const"]["Right"] == 1:
            return []  # This does NOT need to be solved
        if counts["Var"]["Left"] == 0 and counts["Var"][
                "Right"] == 1 and counts["Const"]["Left"] == 1 and counts[
                    "Const"]["Right"] == 0:
            return []  # This does NOT need to be solved
        return plans

    # Determine the final var value after combinations, used to determine if COMBINE is POS/NEG
    final_var_value = 0
    final_var_sign = "negative"
    for varVal in counts["Var"]["Left"]:
        final_var_value += varVal
    for varVal in counts["Var"]["Right"]:
        final_var_value -= varVal
    if final_var_value >= 0:
        final_var_sign = "positive"

    plans = str(plans.state)
    plans = plans[1:len(plans) - 1]
    divide_step = ""
    for plan in plans.split(" & "):
        if debug:
            print("\t\t" + plan)
        # Combine messages
        if "CombineRightConst" in plan:
            steps.append("combine RHS constant terms")
        elif "CombineLeftConst" in plan:
            steps.append("combine LHS constant terms")
        elif "CombineLeftVar" in plan:
            steps.append("combine LHS variable terms and get " +
                         final_var_sign)
        elif "CombineRightVar" in plan:
            steps.append("combine RHS variable terms and get " +
                         final_var_sign)
        elif "DivideLeftVar" in plan:
            val = plan[plan.index("(") + 1:len(plan) - 1]
            divide_step = "divide " + val
        # Add messages
        elif "Add" in plan:
            val = int(plan[plan.index("(") + 1:len(plan) - 1])
            operation = "Add"
            if val > 0:
                val = -val
            #if "Var" in plan:
            step = operation + " " + str(val)
            if "Var" in plan:
                step += "x"
            steps.insert(0, step)

    if divide_step != "" and "1" not in divide_step:
        steps.append(divide_step)

    return steps
Пример #5
0
def solveEquation(equation):
    # Begin Planning Problem
    # Get the string representing the initial state
    left_terms = getLeftTerms(equation)
    right_terms = getRightTerms(equation)
    left_terms = list(filter(None, left_terms))
    right_terms = list(filter(None, right_terms))
    initial_str = getInitialString(left_terms, right_terms)
    terms_list = initial_str.split("&")
    count_const = 0
    count_var = 0
    for x in terms_list:
        if x.find('Const') != -1:
            count_const += 1
        if x.find('Var') != -1:
            count_var += 1

    # There is only a single var
    if count_var == 1:
        terms_list.append(" SingleVar()")
    elif count_var == 2:
        terms_list.append(" TwoVars()")

    # There is only a single const
    if count_const == 1:
        terms_list.append(" SingleConst()")
    elif count_const == 2:
        terms_list.append(" TwoConsts()")

    # Create initial string again
    initial_str = ' &'
    initial_str = initial_str.join(terms_list)
    print(initial_str)
    print()
    # Create the planning problem
    planning_prob = PlanningProblem(
        initial=initial_str,
        goals='Solved()',
        actions=[
            Action(
                'addVar(a)',
                precond='VarRight(a)',
                effect='VarLeft(a) & ~VarRight(a)',
            ),
            # Add a constant to right side from left
            Action(
                'addConst(b)',
                precond='ConstLeft(b)',
                effect='ConstRight(b) & ~ConstLeft(b)',
            ),
            # Combine two consts on the right and replace all with SingleConst()
            Action('combineRightConstTwoTerms(a, b)',
                   precond='ConstRight(a) & ConstRight(b) & TwoConsts()',
                   effect='~ConstRight(a) & ~ConstRight(b) & SingleConst()'),
            # Combine three consts on the right and replace all with SingleConst()
            Action(
                'combineRightConstThreeTerms(a, b, c)',
                precond='ConstRight(a) & ConstRight(b) & ConstRight(c)',
                effect=
                '~ConstRight(a) & ~ConstRight(b) & ~ConstRight(c) & SingleConst()'
            ),
            Action('combineLeftVarTwoTerms(a, b)',
                   precond='VarLeft(a) & VarLeft(b) & TwoVars()',
                   effect='~VarLeft(a) & ~VarLeft(b) & SingleVar()'),
            Action(
                'combineLeftVarThreeTerms(a, b, c)',
                precond='VarLeft(a) & VarLeft(b) & VarLeft(c)',
                effect='~VarLeft(a) & ~VarLeft(b) & ~VarLeft(c) & SingleVar()'
            ),
            # Combine two consts on right where both have duplicate coefficients
            # Action('combineRightDupsTwo(a)',
            #        precond='ConstRight(a) & ConstRight(a)',
            #        effect='~ConstRight(a) & ~ConstRight(a) & ConstRight(b) & SingleConst()'),
            # # Combine three consts on right where all have duplicate coefficients
            # Action('combineRightDupsThree(a)',
            #        precond='ConstRight(a) & ConstRight(a) & ConstRight(a)',
            #        effect='~ConstRight(a) & ~ConstRight(a) & ~ConstRight(a) & ConstRight(b) & SingleConst()'),
            # Divide the SingleVar on left and SingleConst on right return Solved()
            Action(
                'divideNoDup()',
                precond='SingleConst() & SingleVar()',
                effect='Solved() & ~SingleConst() & ~SingleVar()',
            ),
            # Divide SingleVar on left and SingleConst on right with equal coefficients and return Solved()
            Action('divideDup(a)',
                   precond='SingleConst() & SingleVar()',
                   effect='Solved() & ~SingleConst() & ~SingleVar()'),
        ])
    fwd_plan = ForwardPlan(planning_prob)
    solution_str = astar_search(fwd_plan).solution()
    return parseSolution(solution_str, left_terms, right_terms)
Пример #6
0
def read_problem_from_file(filename):
    """
    Reads a planning problem (together with an upper bound t_max on the length
    of plans for this problem) from a file.

    Parameters:
        filename (str): Name of the file that is to be read from.

    Returns:
        ((PlanningProblem,int)): Pair with the planning problem and the
        bound t_max that are read from the file.
    """

    # Auxiliary function to parse a string of the form (prefix + "rest")
    # If string is of this form, it returns True,"rest"
    # Otherwise, it returns False,""
    def remove_prefix_if_possible(line, prefix):
        if line[0:len(prefix)] == prefix:
            return True,line[len(prefix):]
        else:
            return False,""

    try:
        file = open(filename, "r")
        # Initialize
        initial_str = ""
        goals_str = ""
        t_max_str = "20" # default value is 20
        actions_list = []
        # Read the file line by line
        for line in file.readlines():
            stripped_line = line.strip()
            if stripped_line != "" and stripped_line[0] != '#':
                # Lines specifying initial state
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"initial: ")
                if match:
                    initial_str = rest_of_line.strip()
                    if expr(initial_str) == True or expr(initial_str) == None:
                        initial_str = "[]"
                # Lines specifying goals
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"goals: ")
                if match:
                    goals_str = rest_of_line.strip()
                    if expr(goals_str) == True or expr(goals_str) == None:
                        goals_str = "[]"
                # Lines specifying t_max
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"t_max: ")
                if match:
                    t_max_str = rest_of_line.strip()
                # Lines specifying an action
                match,rest_of_line = remove_prefix_if_possible(stripped_line,"action: ")
                if match:
                    action_strs = rest_of_line.split(";")
                    action = Action(action_strs[0], precond=action_strs[1], effect=action_strs[2])
                    if expr(action.precond) == None or expr(action.precond) == True:
                        action.precond = []
                    if expr(action.effect) == None or expr(action.effect) == True:
                        action.effect = []
                    actions_list.append(action)
        # Create planning_problem and t_max from the data stored after reading, and return them
        planning_problem = PlanningProblem(initial=initial_str, goals=goals_str, actions=actions_list)
        t_max = int(t_max_str)
        return planning_problem, t_max

    # If exception occurs, print error message and return None,None
    except Exception as e:
        print("Something went wrong while reading from " + filename + " (" + str(e) + ")")
        return None, None