diff --git a/demo/demo3.py b/demo/demo3.py new file mode 100644 index 0000000000000000000000000000000000000000..088d5ce5e8389cd615fb1868bb879e56bdb93482 --- /dev/null +++ b/demo/demo3.py @@ -0,0 +1,81 @@ +from dryvr_plus_plus.example.example_agent.car_agent import CarAgent +from dryvr_plus_plus.example.example_agent.car_agent import CarAgent +from dryvr_plus_plus.scene_verifier.scenario.scenario import Scenario +from dryvr_plus_plus.example.example_map.simple_map2 import SimpleMap2, SimpleMap3, SimpleMap5, SimpleMap6 +from dryvr_plus_plus.plotter.plotter2D import * +from dryvr_plus_plus.example.example_sensor.fake_sensor import FakeSensor3 + +import matplotlib.pyplot as plt +import numpy as np +from enum import Enum, auto + +class LaneObjectMode(Enum): + Vehicle = auto() + Ped = auto() # Pedestrians + Sign = auto() # Signs, stop signs, merge, yield etc. + Signal = auto() # Traffic lights + Obstacle = auto() # Static (to road/lane) obstacles + +class VehicleMode(Enum): + Normal = auto() + SwitchLeft = auto() + SwitchRight = auto() + Brake = auto() + +class LaneMode(Enum): + Lane0 = auto() + Lane1 = auto() + Lane2 = auto() + +class State: + x = 0.0 + y = 0.0 + theta = 0.0 + v = 0.0 + vehicle_mode: VehicleMode = VehicleMode.Normal + lane_mode: LaneMode = LaneMode.Lane0 + type: LaneObjectMode = LaneObjectMode.Vehicle + + def __init__(self, x, y, theta, v, vehicle_mode: VehicleMode, lane_mode: LaneMode, type: LaneObjectMode): + pass + + +if __name__ == "__main__": + input_code_name = './demo/example_controller4.py' + scenario = Scenario() + + car = CarAgent('car1', file_name=input_code_name) + scenario.add_agent(car) + car = CarAgent('car2', file_name=input_code_name) + scenario.add_agent(car) + car = CarAgent('car3', file_name=input_code_name) + scenario.add_agent(car) + tmp_map = SimpleMap3() + scenario.set_map(tmp_map) + scenario.set_sensor(FakeSensor3()) + scenario.set_init( + [ + [[0, -0.2, 0, 1.0],[0.1, 0.2, 0, 1.0]], + [[10, 0, 0, 0.5],[10, 0, 0, 0.5]], + [[20, 3, 0, 0.5],[20, 3, 0, 0.5]], + ], + [ + (VehicleMode.Normal, LaneMode.Lane1), + (VehicleMode.Normal, LaneMode.Lane1), + (VehicleMode.Normal, LaneMode.Lane0), + ] + ) + res_list = scenario.simulate_multi(40,1) + # traces = scenario.verify(40) + + fig = plt.figure(2) + fig = plot_map(tmp_map, 'g', fig) + # fig = plot_reachtube_tree(traces, 'car1', 1, [2], 'b', fig) + # fig = plot_reachtube_tree(traces, 'car2', 1, [2], 'r', fig) + for traces in res_list: + # generate_simulation_anime(traces, tmp_map, fig) + fig = plot_simulation_tree(traces, 'car1', 1, [2], 'b', fig) + fig = plot_simulation_tree(traces, 'car2', 1, [2], 'r', fig) + fig = plot_simulation_tree(traces, 'car3', 1, [2], 'r', fig) + + plt.show() diff --git a/demo/example_controller4.py b/demo/example_controller4.py new file mode 100644 index 0000000000000000000000000000000000000000..5f7f8f5a1885648d2e7a100b19073961cb6c9450 --- /dev/null +++ b/demo/example_controller4.py @@ -0,0 +1,54 @@ +from enum import Enum, auto +import copy +from typing import List + +class LaneObjectMode(Enum): + Vehicle = auto() + Ped = auto() # Pedestrians + Sign = auto() # Signs, stop signs, merge, yield etc. + Signal = auto() # Traffic lights + Obstacle = auto() # Static (to road/lane) obstacles + +class VehicleMode(Enum): + Normal = auto() + SwitchLeft = auto() + SwitchRight = auto() + Brake = auto() + +class LaneMode(Enum): + Lane0 = auto() + Lane1 = auto() + Lane2 = auto() + +class State: + x = 0.0 + y = 0.0 + theta = 0.0 + v = 0.0 + vehicle_mode: VehicleMode = VehicleMode.Normal + lane_mode: LaneMode = LaneMode.Lane0 + type: LaneObjectMode = LaneObjectMode.Vehicle + + def __init__(self, x, y, theta, v, vehicle_mode: VehicleMode, lane_mode: LaneMode, type: LaneObjectMode): + pass + +def controller(ego:State, others:List[State], lane_map): + output = copy.deepcopy(ego) + if ego.vehicle_mode == VehicleMode.Normal: + if any((other.x-ego.x > 3 and other.x-ego.x < 5 and ego.lane_mode == other.lane_mode) for other in others): + if lane_map.has_left(ego.lane_mode): + output.vehicle_mode = VehicleMode.SwitchLeft + if any((other.x-ego.x > 3 and other.x-ego.x < 5 and ego.lane_mode == other.lane_mode) for other in others): + if lane_map.has_right(ego.lane_mode): + output.vehicle_mode = VehicleMode.SwitchRight + if ego.vehicle_mode == VehicleMode.SwitchLeft: + if lane_map.get_lateral_distance(ego.lane_mode, [ego.x, ego.y]) >= 2.5: + output.vehicle_mode = VehicleMode.Normal + output.lane_mode = lane_map.left_lane(ego.lane_mode) + if ego.vehicle_mode == VehicleMode.SwitchRight: + if lane_map.get_lateral_distance(ego.lane_mode, [ego.x, ego.y]) <= -2.5: + output.vehicle_mode = VehicleMode.Normal + output.lane_mode = lane_map.right_lane(ego.lane_mode) + + return output + diff --git a/develop/parse_any_all.py b/develop/parse_any_all.py index 2caa5cbababa84aa0ddc5bf8408fb083d4d617af..c9c2ff637ed30a8bbf9141bd47020f1764efa3d0 100644 --- a/develop/parse_any_all.py +++ b/develop/parse_any_all.py @@ -55,7 +55,7 @@ def _parse_elt(root, cont_var_dict, disc_var_dict, iter_name_list, targ_name_lis tmp_variable_name = f"{iter_name}_{iter_pos}.{node.attr}" # Replace variables in the etl by using tmp variables - root = AttributeNameSubstituter(tmp_variable_name, node).visit(root) + root = ValueSubstituter(tmp_variable_name, node).visit(root) # Find the value of the tmp variable in the cont/disc_var_dict # Add the tmp variables into the cont/disc_var_dict @@ -83,7 +83,7 @@ def _parse_elt(root, cont_var_dict, disc_var_dict, iter_name_list, targ_name_lis tmp_variable_name = f"{iter_name}_{iter_pos}" # Replace variables in the etl by using tmp variables - root = AttributeNameSubstituter(tmp_variable_name, node).visit(root) + root = ValueSubstituter(tmp_variable_name, node).visit(root) # Find the value of the tmp variable in the cont/disc_var_dict # Add the tmp variables into the cont/disc_var_dict @@ -99,16 +99,16 @@ def _parse_elt(root, cont_var_dict, disc_var_dict, iter_name_list, targ_name_lis # Return the modified node return root -class AttributeNameSubstituter(ast.NodeTransformer): - def __init__(self, name:str, node): +class ValueSubstituter(ast.NodeTransformer): + def __init__(self, val:str, node): super().__init__() - self.name = name + self.val = val self.node = node def visit_Attribute(self, node: ast.Attribute) -> Any: if node == self.node: return ast.Name( - id = self.name, + id = self.val, ctx = ast.Load() ) return node @@ -116,35 +116,31 @@ class AttributeNameSubstituter(ast.NodeTransformer): def visit_Name(self, node: ast.Attribute) -> Any: if node == self.node: return ast.Name( - id = self.name, + id = self.val, ctx = ast.Load ) return node -class FunctionCallSubstituter(ast.NodeTransformer): - def __init__(self, values:List[ast.Expr], node): - super().__init__() - self.values = values - self.node = node - def visit_Call(self, node: ast.Call) -> Any: if node == self.node: if node.func.id == 'any': return ast.BoolOp( - op = ast.Or(), - values = self.values + op = ast.Or(), + values = self.val ) elif node.func.id == 'all': - raise NotImplementedError + return ast.BoolOp( + op = ast.And(), + values = self.val + ) return node -def parse_any( +def parse_any_all( node: ast.Call, cont_var_dict: Dict[str, float], disc_var_dict: Dict[str, float], len_dict: Dict[str, int] ) -> ast.BoolOp: - parse_arg = node.args[0] if isinstance(parse_arg, ast.GeneratorExp): iter_name_list = [] @@ -167,27 +163,56 @@ def parse_any( parsed_elt = _parse_elt(changed_elt, cont_var_dict, disc_var_dict, iter_name_list, targ_name_list, iter_pos_list) # Add the expanded elt into the list expand_elt_ast_list.append(parsed_elt) - # Create the new boolop (or) node based on the list of expanded elt - return FunctionCallSubstituter(expand_elt_ast_list, node).visit(node) - pass + # Create the new boolop (and/or) node based on the list of expanded elt + return ValueSubstituter(expand_elt_ast_list, node).visit(node) + else: + return node + +class NodeSubstituter(ast.NodeTransformer): + def __init__(self, old_node, new_node): + super().__init__() + self.old_node = old_node + self.new_node = new_node + + def visit_Call(self, node: ast.Call) -> Any: + if node == self.old_node: + return self.new_node + else: + return node + +def unroll_any_all(root, cont_var_dict: Dict[str, float], disc_var_dict: Dict[str, float], len_dict: Dict[str, int]) -> None: + i = 0 + while i < sum(1 for _ in ast.walk(root)): + # TODO: Find a faster way to access nodes in the tree + node = list(ast.walk(root))[i] + if isinstance(node, ast.Call) and\ + isinstance(node.func, ast.Name) and\ + (node.func.id=='any' or node.func.id=='all'): + new_node = parse_any_all(node, cont_var_dict, disc_var_dict, len_dict) + root = NodeSubstituter(node, new_node).visit(root) + i += 1 + return root if __name__ == "__main__": - others = [State(), State()] + others = [State(), State(), State(), State()] + ego = State() - code_any = "any((other.x -ego.x > 5 and other.type==Vehicle) for other in others)" + code_any = "any((any(other.y < 100 for other in others) and other.x -ego.x > 5 and other.type==Vehicle) for other in others)" + # code_any = "all((other.x -ego.x > 5 and other.type==Vehicle) for other in others)" ast_any = ast.parse(code_any).body[0].value cont_var_dict = { - "others.x":[1,2], - "others.y":[3,4], + "others.x":[1,2,4,5], + "others.y":[3,4,5,6], "ego.x":5, "ego.y":2 } disc_var_dict = { - "others.type":['Vehicle','Sign'], + "others.type":['Vehicle','Sign','Vehicle','Obs'], "ego.type":'Ped' } len_dict = { - "others":2 + "others":len(others) } - res = parse_any(ast_any, cont_var_dict, disc_var_dict, len_dict) - print(ast_any) + res = unroll_any_all(ast_any, cont_var_dict, disc_var_dict, len_dict) + print(astunparse.unparse(ast_any)) + print(astunparse.unparse(res)) diff --git a/dryvr_plus_plus/example/example_sensor/fake_sensor.py b/dryvr_plus_plus/example/example_sensor/fake_sensor.py index 77b0169057b210bf0fc58068442a9949e3b06c2b..30d4a9854caf92ff9764d977ae7c2921d2154e36 100644 --- a/dryvr_plus_plus/example/example_sensor/fake_sensor.py +++ b/dryvr_plus_plus/example/example_sensor/fake_sensor.py @@ -12,11 +12,30 @@ class FakeSensor1: cnts['ego.v'] = state[4] disc['ego.vehicle_mode'] = mode[0] disc['ego.lane_mode'] = mode[1] - return cnts, disc + return cnts, disc, {} def sets(d, thing, attrs, vals): d.update({thing + "." + k: v for k, v in zip(attrs, vals)}) +def adds(d, thing, attrs, vals): + for k, v in zip(attrs, vals): + if thing + '.' + k not in d: + d[thing + '.' + k] = [v] + else: + d[thing + '.' + k].append(v) + +def add_states_2d(cont, disc, thing, val): + state, mode = val + adds(cont, thing, ['x','y','theta','v'], state[1:5]) + adds(disc, thing, ["vehicle_mode", "lane_mode", "type"], mode) + +def add_states_3d(cont, disc, thing, val): + state, mode = val + transp = np.transpose(np.array(state)[:, 1:5]) + assert len(transp) == 4 + adds(cont, thing, ["x", "y", "theta", "v"], transp) + adds(disc, thing, ["vehicle_mode", "lane_mode", "type"], mode) + def set_states_2d(cnts, disc, thing, val): state, mode = val sets(cnts, thing, ["x", "y", "theta", "v"], state[1:5]) @@ -50,7 +69,7 @@ class FakeSensor2: set_states_2d(cnts, disc, "other", state_dict["car2"]) if "sign" in state_dict: set_states_2d(cnts, disc, "sign", state_dict["sign"]) - return cnts, disc + return cnts, disc, {} else: if agent.id == 'car1': set_states_3d(cnts, disc, "ego", state_dict["car1"]) @@ -67,4 +86,24 @@ class FakeSensor2: set_states_3d(cnts, disc, "other", state_dict["car2"]) if "sign" in state_dict: set_states_3d(cnts, disc, "sign", state_dict["sign"]) - return cnts, disc + return cnts, disc, {} + +class FakeSensor3: + def sense(self, scenario, agent, state_dict, lane_map): + cont = {} + disc = {} + len_dict = {'others':len(state_dict)-1} + tmp = np.array(state_dict['car1'][0]) + if tmp.ndim < 2: + for agent_id in state_dict: + if agent_id == agent.id: + set_states_2d(cont, disc, 'ego', state_dict[agent_id]) + else: + add_states_2d(cont, disc, 'others', state_dict[agent_id]) + else: + for agent_id in state_dict: + if agent_id == agent.id: + set_states_3d(cont, disc, "ego", state_dict[agent_id]) + else: + add_states_3d(cont, disc, 'others', state_dict[agent_id]) + return cont, disc, len_dict \ No newline at end of file diff --git a/dryvr_plus_plus/scene_verifier/automaton/guard.py b/dryvr_plus_plus/scene_verifier/automaton/guard.py index 80d2359e876970f4d1e258580a5cee90fbed7d4b..0324e2919e4d7cc60c93930fef3d64e95b6060d8 100644 --- a/dryvr_plus_plus/scene_verifier/automaton/guard.py +++ b/dryvr_plus_plus/scene_verifier/automaton/guard.py @@ -1,9 +1,7 @@ import enum import re -from typing import List, Dict +from typing import List, Dict, Any import pickle -# from scene_verifier.automaton.hybrid_io_automaton import HybridIoAutomaton -# from pythonparser import Guard import ast import copy @@ -22,6 +20,54 @@ class LogicTreeNode: self.val = val self.mode_guard = mode_guard +class NodeSubstituter(ast.NodeTransformer): + def __init__(self, old_node, new_node): + super().__init__() + self.old_node = old_node + self.new_node = new_node + + def visit_Call(self, node: ast.Call) -> Any: + if node == self.old_node: + return self.new_node + else: + return node + +class ValueSubstituter(ast.NodeTransformer): + def __init__(self, val:str, node): + super().__init__() + self.val = val + self.node = node + + def visit_Attribute(self, node: ast.Attribute) -> Any: + if node == self.node: + return ast.Name( + id = self.val, + ctx = ast.Load() + ) + return node + + def visit_Name(self, node: ast.Attribute) -> Any: + if node == self.node: + return ast.Name( + id = self.val, + ctx = ast.Load + ) + return node + + def visit_Call(self, node: ast.Call) -> Any: + if node == self.node: + if node.func.id == 'any': + return ast.BoolOp( + op = ast.Or(), + values = self.val + ) + elif node.func.id == 'all': + return ast.BoolOp( + op = ast.And(), + values = self.val + ) + return node + class GuardExpressionAst: def __init__(self, guard_list): self.ast_list = [] @@ -659,9 +705,133 @@ class GuardExpressionAst: return -val else: raise ValueError(f'Node type {root} from {astunparse.unparse(root)} is not supported') + elif isinstance(root, ast.Name): + variable = root.id + if variable in cnts_var_dict: + val = cnts_var_dict[variable] + return val + elif variable in disc_var_dict: + val = disc_var_dict[variable] + for mode_name in agent.controller.modes: + if val in agent.controller.modes[mode_name]: + val = mode_name+'.'+val + break + return val + else: + raise ValueError(f"{variable} doesn't exist in either continuous varibales or discrete variables") else: raise ValueError(f'Node type {root} from {astunparse.unparse(root)} is not supported') + def parse_any_all(self, cont_var_dict: Dict[str, float], disc_var_dict: Dict[str, float], len_dict: Dict[str, int]) -> None: + for i in range(len(self.ast_list)): + root = self.ast_list[i] + j = 0 + while j < sum(1 for _ in ast.walk(root)): + # TODO: Find a faster way to access nodes in the tree + node = list(ast.walk(root))[j] + if isinstance(node, ast.Call) and\ + isinstance(node.func, ast.Name) and\ + (node.func.id=='any' or node.func.id=='all'): + new_node = self.unroll_any_all(node, cont_var_dict, disc_var_dict, len_dict) + root = NodeSubstituter(node, new_node).visit(root) + j += 1 + self.ast_list[i] = root + + def unroll_any_all( + self, node: ast.Call, + cont_var_dict: Dict[str, float], + disc_var_dict: Dict[str, float], + len_dict: Dict[str, float] + ) -> ast.BoolOp: + parse_arg = node.args[0] + if isinstance(parse_arg, ast.GeneratorExp): + iter_name_list = [] + targ_name_list = [] + iter_len_list = [] + # Get all the iter, targets and the length of iter list + for generator in parse_arg.generators: + iter_name_list.append(generator.iter.id) # a_list + targ_name_list.append(generator.target.id) # a + iter_len_list.append(range(len_dict[generator.iter.id])) # len(a_list) + + elt = parse_arg.elt + expand_elt_ast_list = [] + iter_len_list = list(itertools.product(*iter_len_list)) + # Loop through all possible combination of iter value + for i in range(len(iter_len_list)): + changed_elt = copy.deepcopy(elt) + iter_pos_list = iter_len_list[i] + # substitute temporary variable in each of the elt and add corresponding variables in the variable dicts + parsed_elt = self._parse_elt(changed_elt, cont_var_dict, disc_var_dict, iter_name_list, targ_name_list, iter_pos_list) + # Add the expanded elt into the list + expand_elt_ast_list.append(parsed_elt) + # Create the new boolop (and/or) node based on the list of expanded elt + return ValueSubstituter(expand_elt_ast_list, node).visit(node) + else: + return node + + def _parse_elt(self, root, cont_var_dict, disc_var_dict, iter_name_list, targ_name_list, iter_pos_list) -> Any: + # Loop through all node in the elt ast + for node in ast.walk(root): + # If the node is an attribute + if isinstance(node, ast.Attribute): + if node.value.id in targ_name_list: + # Find corresponding targ_name in the targ_name_list + targ_name = node.value.id + var_index = targ_name_list.index(targ_name) + + # Find the corresponding iter_name in the iter_name_list + iter_name = iter_name_list[var_index] + + # Create the name for the tmp variable + iter_pos = iter_pos_list[var_index] + tmp_variable_name = f"{iter_name}_{iter_pos}.{node.attr}" + + # Replace variables in the etl by using tmp variables + root = ValueSubstituter(tmp_variable_name, node).visit(root) + + # Find the value of the tmp variable in the cont/disc_var_dict + # Add the tmp variables into the cont/disc_var_dict + variable_name = iter_name + '.' + node.attr + variable_val = None + if variable_name in cont_var_dict: + variable_val = cont_var_dict[variable_name][iter_pos] + cont_var_dict[tmp_variable_name] = variable_val + elif variable_name in disc_var_dict: + variable_val = disc_var_dict[variable_name][iter_pos] + disc_var_dict[tmp_variable_name] = variable_val + + if isinstance(node, ast.Name): + if node.id in targ_name_list: + node:ast.Name + # Find corresponding targ_name in the targ_name_list + targ_name = node.id + var_index = targ_name_list.index(targ_name) + + # Find the corresponding iter_name in the iter_name_list + iter_name = iter_name_list[var_index] + + # Create the name for the tmp variable + iter_pos = iter_pos_list[var_index] + tmp_variable_name = f"{iter_name}_{iter_pos}" + + # Replace variables in the etl by using tmp variables + root = ValueSubstituter(tmp_variable_name, node).visit(root) + + # Find the value of the tmp variable in the cont/disc_var_dict + # Add the tmp variables into the cont/disc_var_dict + variable_name = iter_name + variable_val = None + if variable_name in cont_var_dict: + variable_val = cont_var_dict[variable_name][iter_pos] + cont_var_dict[tmp_variable_name] = variable_val + elif variable_name in disc_var_dict: + variable_val = disc_var_dict[variable_name][iter_pos] + disc_var_dict[tmp_variable_name] = variable_val + + # Return the modified node + return root + if __name__ == "__main__": with open('tmp.pickle','rb') as f: guard_list = pickle.load(f) diff --git a/dryvr_plus_plus/scene_verifier/code_parser/pythonparser.py b/dryvr_plus_plus/scene_verifier/code_parser/pythonparser.py index e3201422fda74b63148f60ef0444aac1d50fe521..a8cf1f8525397c07caff585fad82a1b343c7ec2a 100644 --- a/dryvr_plus_plus/scene_verifier/code_parser/pythonparser.py +++ b/dryvr_plus_plus/scene_verifier/code_parser/pythonparser.py @@ -73,12 +73,11 @@ class Guard(Statement): return Guard(ast.get_source_segment(code, node.test), None, None, node.test) elif isinstance(node.test, ast.Call): source_segment = ast.get_source_segment(code, node.test) - if "map" in source_segment: - func = node.test.func.value.id + '.' + node.test.func.attr - args = [] - for arg in node.test.args: - args.append(arg.value.id + '.' + arg.attr) - return Guard(source_segment, None, None, node.test, func, args) + # func = node.test.func.id + # args = [] + # for arg in node.test.args: + # args.append(arg.value.id + '.' + arg.attr) + return Guard(source_segment, None, None, node.test) ''' Reset class. Subclass of statement. @@ -408,9 +407,17 @@ class ControllerAst(): for arg in args: if arg.annotation is None: continue - if arg.annotation.id not in state_object_dict: - continue - arg_annotation = arg.annotation.id + # Handle case when input is a single variable + if isinstance(arg.annotation, ast.Name): + if arg.annotation.id not in state_object_dict: + continue + arg_annotation = arg.annotation.id + # Handle case when input is a list of variables + elif isinstance(arg.annotation, ast.Subscript): + if arg.annotation.slice.value.id not in state_object_dict: + continue + arg_annotation = arg.annotation.slice.value.id + arg_name = arg.arg vars_dict[arg_name] = {'cont':[], 'disc':[], "type": []} for var in state_object_dict[arg_annotation]['cont']: @@ -423,11 +430,6 @@ class ControllerAst(): type_vars.append(arg_name+"."+var) vars_dict[arg_name]['type'].append(var) - # if "mode" not in arg.arg: - # vars.append(arg.arg) - # #todo: what to add for return values - # else: - # discrete_vars.append(arg.arg) return [statementtree, vars, mode_dict, discrete_vars, state_object_dict, vars_dict, type_vars] diff --git a/dryvr_plus_plus/scene_verifier/scenario/scenario.py b/dryvr_plus_plus/scene_verifier/scenario/scenario.py index 715e836cc7648d079f558c745196dc6c3aff795c..c2ef90431c25106b2d94357facbceba7e6c0d0e6 100644 --- a/dryvr_plus_plus/scene_verifier/scenario/scenario.py +++ b/dryvr_plus_plus/scene_verifier/scenario/scenario.py @@ -122,6 +122,8 @@ class Scenario: # TODO: Handle hybrid guards that involves both continuous and discrete dynamics # Will have to limit the amount of hybrid guards that we want to handle. The difficulty will be handle function guards. guard_can_satisfied = guard_expression.evaluate_guard_hybrid(agent, discrete_variable_dict, continuous_variable_dict, self.map) + if not guard_can_satisfied: + continue # Handle guards realted only to continuous variables using SMT solvers. These types of guards can be pretty general guard_satisfied, is_contained = guard_expression.evaluate_guard_cont(agent, continuous_variable_dict, self.map) @@ -220,7 +222,10 @@ class Scenario: # guard_expression = GuardExpression(guard_list=guard_list) guard_expression = GuardExpressionAst(guard_list) # Map the values to variables using sensor - continuous_variable_dict, discrete_variable_dict = self.sensor.sense(self, agent, state_dict, self.map) + continuous_variable_dict, discrete_variable_dict, length_dict = self.sensor.sense(self, agent, state_dict, self.map) + + # Unroll all the any/all functions in the guard + guard_expression.parse_any_all(continuous_variable_dict, discrete_variable_dict, length_dict) '''Execute functions related to map to see if the guard can be satisfied''' '''Check guards related to modes to see if the guards can be satisfied'''