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;
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)
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
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)
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