Source code for verigym.frameworks.prism.prismpolicy

from verigym.policy.policy import PolicyClass
from verigym.abstraction.abstractionmapper import AbstractionMapper
from warnings import warn

[docs] class PrismPolicy(PolicyClass): """ For PrismPolicy, the external policy object is a file. Notes ----- 1. Assumes that the policy file was generated in PRISM with the option states=false. Otherwise, states are represented by valuations instead of indices, which cannot be mapped back using the abstraction mapper. 2. Assumes memoryless deterministic strategies. """ def __init__(self, policy_path: str, abstraction_mapper: AbstractionMapper, action_map: dict = None ): """ Initializes a policy from PRISM-readable output. Parameters: policy : str The path to the PRISM policy output file. Should be a `.tra` file. We currently do not support `.dot` files. abstraction_mapper : AbstractionMapper Maps the state/action spaces of the PRISM model to the gym environment to deploy the policy on. action_map : dict(str: int) A mapping from PRISM action label to discrete action index in the gym space. """ parsed_policy = self._init_policy(policy_path) # Use action mapping if given, otherwise treat labels as indexes if action_map is None: self.action_label_to_idx = lambda label: int(label) elif isinstance(action_map, dict): self.action_label_to_idx = lambda label: action_map[label] super().__init__(policy=parsed_policy, abstraction_mapper=abstraction_mapper) def _init_policy(self, policyfile): parsed_policy = {} with open(policyfile, "r") as pf: policy_str = pf.readlines() if policyfile.endswith(".dot"): raise NotImplementedError("We currently do not support .dot policies.") # These behave a bit weird, it looks like they summarize states with the same behaviors for visualization purposes. # remove empty line at the end to avoid parsing errors if len(policy_str[-1]) == 0: policy_str = policy_str[:-1] elif policyfile.endswith(".tra"): model_info = policy_str[0].split(" ") n_states = int(model_info[0]) policy_str = policy_str[1:] # the first row just shows n states and n choices for line in policy_str: line_list = line.strip().split(" ") # each line is: state idx, next state idx, prob, action label state = int(line_list[0]) action_label = line_list[3] parsed_policy[state] = action_label assert len(parsed_policy.keys()) == n_states, f"states in policy: {len(parsed_policy.keys())}, states in model: {n_states}" else: # action list for line in policy_str: line_list = line.strip().split("=") state = int(line_list[0]) action_label = line_list[1] parsed_policy[state] = action_label return parsed_policy def _action_from_policy(self, obs): obs_enum = self.abstraction_mapper._state_abstraction_map.abstract_to_enum(obs) if obs_enum not in self.policy.keys(): warn(f"Abstract state {obs} has no action in this policy: state may be terminal or unreachable.") action_enum = 0 # default else: prism_action = self.policy[obs_enum] action_enum = self.action_label_to_idx(prism_action) action = self.abstraction_mapper._action_abstraction_map.enum_to_abstract(action_enum) return action