Action Sequence Graph Tutorial
This tutorial covers use of the ActionArchitecture in the Function class, which is useful for representing a Function’s progress through an action sequence graph, the different actions/tasks to be performed by a function (e.g., modes of operation, etc)..
Copyright © 2024, United States Government, as represented by the Administrator of the National Aeronautics and Space Administration. All rights reserved.
The “"Fault Model Design tools - fmdtools version 2"” software is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
[1]:
import fmdtools.sim.propagate as prop
import inspect
Action architectures are used within a function block to represent Action Sequence Graphs - actions that the function performs and their sequence. Actions in an ActionArchitecture are respresented by Action
blocks, which are similar to function and component blocks in that they have: - flow connections - modes, and - behaviors
Flow connections are routed to the action in the function block definition and represent the shared variables between the actions.
Modes are similar to function modes and are instantiated (as they are in components) at both the Function and Action level. Using the name=
option enables one to tag these modes as action modes at the function level while using the same local name
Below we define the states to performed by the ASG: - Outcome, which tracks the number of actions and perceptions performed by the ASG - HazardState, which specifies whether a hazard is present, percieved, or mitigated
[2]:
from demo_model import OutcomeStates, Outcome, HazardState, Hazard
print(inspect.getsource(OutcomeStates))
print(inspect.getsource(Outcome))
print(inspect.getsource(HazardState))
print(inspect.getsource(Hazard))
class OutcomeStates(State):
"""State tracking the number of actions and perceptions performed."""
num_perceptions: int = 0
num_actions: int = 0
class Outcome(Flow):
__slots__ = ()
container_s = OutcomeStates
class HazardState(State):
"""Whether a hazard is present, percieved, or mitigated by the human."""
present: bool = False
percieved: bool = False
mitigated: bool = False
class Hazard(Flow):
__slots__ = ()
container_s = HazardState
Below we define three actions for use in a given model: - Perceive, a user’s perception abilities/behaviors. In this function the user percieves a hazard (unless their perception fails) - Act, the user’s action which they perform to mitigate the hazard. - Done, the user’s state when they are done performing the action.
[3]:
from examples.asg_demo.demo_model import Perceive, Act, Done
print(inspect.getsource(Perceive))
print(inspect.getsource(Act))
print(inspect.getsource(Done))
class Perceive(Action):
"""A user's perception abilities/behaviors for percieving the hazard."""
__slots__ = ('hazard', 'outcome')
container_m = ActionMode
flow_hazard = Hazard
flow_outcome = Outcome
def behavior(self, time):
if not self.m.in_mode('failed', 'unable'):
self.hazard.s.percieved = self.hazard.s.present
self.outcome.s.num_perceptions += self.hazard.s.percieved
else:
self.hazard.s.percieved = False
self.m.remove_fault('failed', 'nom')
def percieved(self):
return self.hazard.s.percieved
class Act(Action):
"""User actions to mitigate the hazard."""
__slots__ = ('hazard', 'outcome')
container_m = ActionMode
flow_hazard = Hazard
flow_outcome = Outcome
def behavior(self, time):
if not self.m.in_mode('failed', 'unable'):
self.outcome.s.num_actions += 1
self.hazard.s.mitigated = True
elif self.m.in_mode('failed'):
self.hazard.s.mitigated = False
self.m.remove_fault('failed', 'nom')
else:
self.hazard.s.mitigated = False
def acted(self):
return not self.m.in_mode('failed')
class Done(Action):
"""User state after performing the action."""
__slots__ = ('hazard')
flow_hazard = Hazard
def behavior(self, time):
if not self.hazard.s.present:
self.hazard.s.mitigated = False
def ready(self):
return not self.hazard.s.present
To proceed through the sequence of actions, conditions must be met between each action. In these actions, we have defined the following conditions: - Percieve.percieved: perception is done if the hazard is percieved - Act.acted: the action is complete if the action was performed - Done.complete: the hazard mitigation is over (and mitigated state is reset to False)
To create the overall ASG structure, the following adds the flows, actions, and conditions to the ActionArchitecture class
[4]:
from demo_model import Human
print(inspect.getsource(Human))
class Human(ActionArchitecture):
"""Overall human action sequence graph specifying what the user will do when."""
initial_action = "perceive"
def init_architecture(self, *args, **kwargs):
# flows from external fxn/model can be defined here,
self.add_flow("hazard", Hazard)
# along with flows internal to the ASG class
self.add_flow("outcome", fclass=Outcome)
self.add_act("perceive", Perceive, "outcome", "hazard")
self.add_act("act", Act, "outcome", "hazard")
self.add_act("done", Done, "hazard")
self.add_cond("perceive", "act", "percieved", self.acts['perceive'].percieved)
self.add_cond("act", "done", "acted", self.acts['act'].acted)
self.add_cond("done", "perceive", "ready", self.acts['done'].ready)
Note the use of the following methods: - add_flow adds a flow so it can pass variables between actions. Here Outcome is an internal flow, while Hazard is an external flow.
[5]:
from fmdtools.define.architecture.action import ActionArchitecture
help(ActionArchitecture.add_flow)
Help on function add_flow in module fmdtools.define.architecture.base:
add_flow(self, name, fclass=<class 'fmdtools.define.flow.base.Flow'>, **kwargs)
Add a flow with given attributes to the model.
Parameters
----------
name : str
Unique flow name to give the flow in the model
fclass : Class, optional
Class to instantiate (e.g. CommsFlow, MultiFlow). Default is Flow.
Class must take flowname, p, s as input to __init__()
May alternatively provide already-instanced object.
kwargs: kwargs
Dicts for non-default values to p, s, etc
add_act adds the action to the function and hands it the given flows and parameters. Here the actions are “Percieve”, “Act”, and “Done”
[6]:
help(ActionArchitecture.add_act)
Help on function add_act in module fmdtools.define.architecture.action:
add_act(self, name, actclass, *flownames, **fkwargs)
Associate an Action with the architecture. Called after add_flow.
Parameters
----------
name : str
Internal Name for the Action
actclass : Action
Action class to instantiate
*flownames : flow
Flows (optional) which connect the actions
duration:
Duration of the action. Default is 0.0
**kwargs : any
kwargs to instantiate the Action with.
add_cond specifies the conditions for going from one action to another.
[7]:
help(ActionArchitecture.add_cond)
Help on function add_cond in module fmdtools.define.architecture.action:
add_cond(self, start_action, end_action, name='auto', condition='pass')
Associate a Condition with the ActionArchitecture.
Conditions specify when to precede from one action to the next.
Parameters
----------
start_action : str
Action where the condition is checked
end_action : str
Action that the condition leads to.
name : str
Name for the condition.
Defaults to numbered conditions if none are provided.
condition : method
Method in the class to use as a condition.
Defaults to self.condition_pass if none are provided.
ASG.build finally constructs the structure of the ASG (see: self.action_graph and self.flow_graph) and determines the settings for the simulation. In DetectHazard, default options are used, with the first action specified as “Percieve” and also with it specified that the actions propagate in the dynamic step (rather than static step)
[8]:
help(ActionArchitecture.build)
Help on function build in module fmdtools.define.architecture.action:
build(self, **kwargs)
Construct the overall model structure.
Use in subclasses to build the model after init_architecture is called.
Parameters
----------
update_seed : bool
Whether to update the seed
We can look at the attributes of the ASG
by instantiating it:
[9]:
h = Human()
h
[9]:
<demo_model.Human at 0x1ac1c895130>
ActionArchitectureGraph
can also be used to visualize the flow of actions (e.g., which are active, flows vs conditions, etc.).
This graph can also be generated using ActionArchitecture.as_modelgraph()
[10]:
from fmdtools.define.architecture.action import ActionArchitectureGraph
[11]:
ag = ActionArchitectureGraph(h)
[12]:
fig, ax = ag.draw()
As shown, the “Percieve” action is active (green), while the inactive actions are shown in blue. This action is active because it was defined as initial_action in the ASG definition.
These are stored as attributes in the underlying graph structure:
[13]:
ag.g.nodes['perceive']
[13]:
{'bipartite': 0, 'nodetype': 'Action', 'active': True}
Function-level Simulation
ActionArchitecture
s must be instantiated within a Function
in order to be simulable with methods in propagate
. Below we instantiate a Function
and show how an ActionArchitecture
can be simulated independent of other Model attributes.
[14]:
from demo_model import DetectHazard
print(inspect.getsource(DetectHazard))
class DetectHazard(Function):
"""Function containing the human."""
__slots__ = ('hazard')
container_m = Mode
arch_aa = Human
flow_hazard = Hazard
If we update the action, we can see the ASG progress between states:
[15]:
ex_fxn = DetectHazard('detect_hazard')
ex_fxn.t.dt=1.0
ex_fxn.aa.flows['hazard']
[15]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[16]:
ex_fxn.aa.flows['hazard'].s.present=True
ex_fxn('dynamic', time= 1)
[17]:
ag = ActionArchitectureGraph(ex_fxn.aa)
ag.draw()
[17]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: >)
[18]:
ex_fxn.aa.flows['hazard']
[18]:
hazard Hazard flow: HazardState(present=True, percieved=True, mitigated=True)
[19]:
assert ex_fxn.aa.flows['hazard'].s.present
[20]:
ex_fxn.aa.flows['outcome']
[20]:
outcome Outcome flow: OutcomeStates(num_perceptions=1, num_actions=1)
[21]:
assert ex_fxn.aa.flows['outcome'].s.num_perceptions==1
assert ex_fxn.aa.flows['outcome'].s.num_actions==1
As shown, each of the actions are progressed throuh in a single timestep until the ASG is in the “Done” action
[22]:
ex_fxn.aa.flows['hazard'].s.present=False
ex_fxn('dynamic', time= 2)
[23]:
ag = ActionArchitectureGraph(ex_fxn.aa)
fig, ax = ag.draw()
As shown, now that the hazard is no longer present, the “Ready” Condition is triggered and the ASG goes back to the percieve state.
[24]:
ex_fxn.aa.flows['outcome']
[24]:
outcome Outcome flow: OutcomeStates(num_perceptions=1, num_actions=1)
[25]:
ex_fxn.aa.flows['hazard']
[25]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[26]:
ex_fxn.hazard
[26]:
hazard Hazard flow: HazardState(present=False, percieved=False, mitigated=False)
[27]:
assert ex_fxn.aa.flows['hazard'].s.present==False
assert ex_fxn.aa.flows['hazard'].s.mitigated==False
assert id(ex_fxn.hazard) == id(ex_fxn.aa.flows['hazard'])
This is essentially what a step-though of a simulation looks like. In practice, we simulate these behaviors using propagate
as long as the ActionArchitecture
is contained in a Function
. Here we use the disturbances
argument to simulate an external hazard occuring at t=5
:
[28]:
result_indiv, hist_indiv = prop.nominal(ex_fxn, disturbances={5:{'aa.flows.hazard.s.present':True}})
[29]:
fig, axs = hist_indiv.plot_line('aa.flows.hazard.s.present',
'aa.flows.hazard.s.percieved',
'aa.flows.hazard.s.mitigated', figsize=(10,5))
[30]:
hist_indiv.aa.active_actions
[30]:
array([{'perceive'}, {'perceive'}, {'perceive'}, {'perceive'},
{'perceive'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}, {'done'}, {'done'}, {'done'}, {'done'}, {'done'},
{'done'}], dtype=object)
[31]:
assert hist_indiv.aa.flows.hazard.s.mitigated[5]
assert hist_indiv.aa.active_actions[5]=={'done'}
Model Simulation
Below, this function is placed in the context of a FunctionalArchitecture model so we can see how it behaves in the context of its interactions.
[32]:
from demo_model import ProduceHazard, PassStates, PassHazard, HazardModel
print(inspect.getsource(ProduceHazard))
print(inspect.getsource(PassStates))
print(inspect.getsource(PassHazard))
print(inspect.getsource(HazardModel))
class ProduceHazard(Function):
"""Function producing Hazards."""
__slots__ = ('hazard',)
flow_hazard = Hazard
def dynamic_behavior(self, time):
if not time % 4:
self.hazard.s.present = True
else:
self.hazard.s.present = False
class PassStates(State):
"""Whether or not the hazard is ultimately passed or mitigated."""
hazards_mitigated: int = 0
hazards_propagated: int = 0
class PassHazard(Function):
"""Accumulates total hazards/mitigations."""
__slots__ = ('hazard',)
container_s = PassStates
flow_hazard = Hazard
def dynamic_behavior(self, time):
if self.hazard.s.present and self.hazard.s.mitigated:
self.s.hazards_mitigated += 1
elif self.hazard.s.present and not self.hazard.s.mitigated:
self.s.hazards_propagated += 1
class HazardModel(FunctionArchitecture):
"""Overall model of the human in context."""
__slots__ = ()
default_sp = dict(end_time=60, dt=1.0)
def init_architecture(self, **kwargs):
self.add_flow("hazard", Hazard)
self.add_fxn("produce_hazard", ProduceHazard, 'hazard')
self.add_fxn("detect_hazard", DetectHazard, 'hazard')
self.add_fxn("pass_hazard", PassHazard, 'hazard')
As shown, this model connects the detect_hazard
with a detect_hazard
function, which are used (1) to load the system and then to (2) tabulate the number of mitigations.
Below we show how this translates into simulation results:
[33]:
mdl = HazardModel()
endstate, mdlhist = prop.nominal(mdl)
Below we look at the states of the functions/flows to see how this has simulated.
[34]:
mdlhist
[34]:
flows.hazard.s.present: array(61)
flows.hazard.s.percieved: array(61)
flows.hazard.s.mitigated: array(61)
fxns.detect_hazard.aa.active_actions: array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.aa.acts.act.m.mode: array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.a array(61)
fxns.detect_hazard.m array(61)
fxns.detect_hazard.m array(61)
fxns.detect_hazard.m.faults.act_failed: array(61)
fxns.detect_hazard.m.faults.act_unable: array(61)
fxns.pass_hazard.s.hazards_mitigated: array(61)
fxns.pass_hazard.s.hazards_propagated: array(61)
time: array(61)
[35]:
restab = mdlhist.get('fxns.detect_hazard.aa.active_actions', 'flows.hazard.s').as_table()
[36]:
restab
[36]:
fxns.detect_hazard.aa.active_actions | flows.hazard.s.present | flows.hazard.s.percieved | flows.hazard.s.mitigated | |
---|---|---|---|---|
0 | {done} | True | True | True |
1 | {perceive} | False | False | False |
2 | {perceive} | False | False | False |
3 | {perceive} | False | False | False |
4 | {done} | True | True | True |
... | ... | ... | ... | ... |
56 | {done} | True | True | True |
57 | {perceive} | False | False | False |
58 | {perceive} | False | False | False |
59 | {perceive} | False | False | False |
60 | {done} | True | True | True |
61 rows × 4 columns
As shown, the ASG alternates between Perceive (when the hazard is not present) and Done (when the hazard is present). As a result, all of the present hazards (above) are also perceived and mitigated, and no hazards are propagated.
Or, in plot form:
[37]:
fig, axs = mdlhist.plot_line('fxns.pass_hazard.s.hazards_mitigated',
'fxns.pass_hazard.s.hazards_propagated',
'flows.hazard.s.present',
'flows.hazard.s.percieved',
'flows.hazard.s.mitigated', figsize=(10,5))
As shown, perceptions and actions track the hazards mitigated.
Fault Simulation
Below we will simulate a fault and see how it tracks in the model.
[38]:
result_fault, mdlhist_fault = prop.one_fault(mdl, 'detect_hazard','perceive_failed', time=4, desired_result='graph')
[39]:
mdlhist_fault.faulty.fxns.detect_hazard.aa.acts.act
[39]:
m.mode: array(61)
m.faults.failed: array(61)
m.faults.unable: array(61)
As shown, this fault results in the hazard not being perceived (and thus the hazard propagating)
[40]:
fig, axs = mdlhist_fault.plot_line('fxns.pass_hazard.s.hazards_mitigated',
'fxns.pass_hazard.s.hazards_propagated',
'flows.hazard.s.present',
'flows.hazard.s.percieved',
'flows.hazard.s.mitigated', figsize=(10,5), time_slice=[4])
As shown, this only shows up in the PassHazard function (since the fault is removed in one timestep).
[41]:
result_fault.graph.draw()
[41]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: >)
To see this in more detail, we will process the results history and then use graph.results_from
at the time of the fault. Note how the model histories can be processed below:
[42]:
reshist = mdlhist_fault.get_degraded_hist(*mdl.fxns, *mdl.flows)
reshist.as_table()
[42]:
detect_hazard | pass_hazard | hazard | total | time | |
---|---|---|---|---|---|
0 | False | False | False | 3 | 0.0 |
1 | False | False | False | 3 | 1.0 |
2 | False | False | False | 3 | 2.0 |
3 | False | False | False | 3 | 3.0 |
4 | True | True | True | 0 | 4.0 |
... | ... | ... | ... | ... | ... |
56 | True | True | True | 0 | 56.0 |
57 | True | True | True | 0 | 57.0 |
58 | True | True | True | 0 | 58.0 |
59 | True | True | True | 0 | 59.0 |
60 | True | True | True | 0 | 60.0 |
61 rows × 5 columns
[43]:
reshist = mdlhist_fault.get_faulty_hist(*mdl.fxns, *mdl.flows)
reshist.as_table()
[43]:
detect_hazard | total | time | |
---|---|---|---|
0 | False | 0 | 0.0 |
1 | False | 0 | 1.0 |
2 | False | 0 | 2.0 |
3 | False | 0 | 3.0 |
4 | False | 0 | 4.0 |
... | ... | ... | ... |
56 | False | 0 | 56.0 |
57 | False | 0 | 57.0 |
58 | False | 0 | 58.0 |
59 | False | 0 | 59.0 |
60 | False | 0 | 60.0 |
61 rows × 3 columns
[44]:
mdlhist_fault.faulty.fxns.detect_hazard
[44]:
aa.active_actions: array(61)
aa.flows.hazard.s.present: array(61)
aa.flows.hazard.s.percieved: array(61)
aa.flows.hazard.s.mitigated: array(61)
aa.flows.outcome.s.num_perceptions: array(61)
aa.flows.outcome.s.num_actions: array(61)
aa.acts.perceive.m.mode: array(61)
aa.acts.perceive.m.faults.failed: array(61)
aa.acts.perceive.m.faults.unable: array(61)
aa.acts.act.m.mode: array(61)
aa.acts.act.m.faults.failed: array(61)
aa.acts.act.m.faults.unable: array(61)
m.faults.perceive_failed: array(61)
m.faults.perceive_unable: array(61)
m.faults.act_failed: array(61)
m.faults.act_unable: array(61)
[45]:
mdl.fxns['detect_hazard'].h
[45]:
aa.active_actions: array(61)
aa.flows.hazard.s.present: array(61)
aa.flows.hazard.s.percieved: array(61)
aa.flows.hazard.s.mitigated: array(61)
aa.flows.outcome.s.num_perceptions: array(61)
aa.flows.outcome.s.num_actions: array(61)
aa.acts.perceive.m.mode: array(61)
aa.acts.perceive.m.faults.failed: array(61)
aa.acts.perceive.m.faults.unable: array(61)
aa.acts.act.m.mode: array(61)
aa.acts.act.m.faults.failed: array(61)
aa.acts.act.m.faults.unable: array(61)
m.faults.perceive_failed: array(61)
m.faults.perceive_unable: array(61)
m.faults.act_failed: array(61)
m.faults.act_unable: array(61)
We can further use draw_from
to view the state of the ASG. See below:
[46]:
result_fault.graph.draw_from(4, mdlhist_fault)
[46]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: title={'center': ' t=4'}>)
Note the lack of a fault at this time-step, despite it being instantiated here. This is because the fault was removed at the end of the same time-step it was added in.
The ‘unable’ fault, on the other hand, stays throughout the simulation and thus shows up:
[47]:
endstate_unable, mdlhist_unable = prop.one_fault(mdl, 'detect_hazard','act_unable', time=4,
desired_result={'graph','graph.fxns.detect_hazard.aa'})
fig, axs = mdlhist_unable.plot_line('fxns.pass_hazard.s.hazards_mitigated',
'fxns.pass_hazard.s.hazards_propagated',
'flows.hazard.s.present',
'flows.hazard.s.percieved',
'flows.hazard.s.mitigated',
'fxns.detect_hazard.aa.flows.hazard.s.percieved',
'fxns.detect_hazard.aa.acts.act.m.faults.unable',
figsize=(10, 5), time_slice=[4])
[48]:
mdlhist_unable.faulty.fxns.detect_hazard.aa.active_actions
[48]:
array([{'done'}, {'perceive'}, {'perceive'}, {'perceive'}, {'done'},
{'perceive'}, {'perceive'}, {'perceive'}, {'done'}, {'perceive'},
{'perceive'}, {'perceive'}, {'done'}, {'perceive'}, {'perceive'},
{'perceive'}, {'done'}, {'perceive'}, {'perceive'}, {'perceive'},
{'done'}, {'perceive'}, {'perceive'}, {'perceive'}, {'done'},
{'perceive'}, {'perceive'}, {'perceive'}, {'done'}, {'perceive'},
{'perceive'}, {'perceive'}, {'done'}, {'perceive'}, {'perceive'},
{'perceive'}, {'done'}, {'perceive'}, {'perceive'}, {'perceive'},
{'done'}, {'perceive'}, {'perceive'}, {'perceive'}, {'done'},
{'perceive'}, {'perceive'}, {'perceive'}, {'done'}, {'perceive'},
{'perceive'}, {'perceive'}, {'done'}, {'perceive'}, {'perceive'},
{'perceive'}, {'done'}, {'perceive'}, {'perceive'}, {'perceive'},
{'done'}], dtype=object)
[49]:
mdl.fxns['detect_hazard'].h.aa.acts.perceive
[49]:
m.mode: array(61)
m.faults.failed: array(61)
m.faults.unable: array(61)
[50]:
mdl.fxns['detect_hazard'].aa.create_hist([0.0])
[50]:
acts.perceive: array(1)
acts.act: array(1)
acts.done: array(1)
active_actions: array(1)
[51]:
mdl.fxns['detect_hazard'].aa.track
[51]:
['acts', 'flows', 'active_actions', 'i']
[52]:
mdl.fxns['detect_hazard'].aa.h.acts.perceive.m.faults.failed
[52]:
array([False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False])
[53]:
mdlhist_unable.nominal.fxns.detect_hazard.aa.acts
[53]:
perceive.m.mode: array(61)
perceive.m.faults.failed: array(61)
perceive.m.faults.unable: array(61)
act.m.mode: array(61)
act.m.faults.failed: array(61)
act.m.faults.unable: array(61)
[54]:
endstate_unable.graph.draw_from(4, mdlhist_unable)
[54]:
(<Figure size 1200x1000 with 1 Axes>, <Axes: title={'center': ' t=4'}>)
[55]:
fig, ax = endstate_unable['graph.fxns.detect_hazard.aa'].draw_from(0, mdlhist_unable)
[56]:
fig, ax = endstate_unable['graph.fxns.detect_hazard.aa'].draw_from(5, mdlhist_unable, rem_ind=0)
[ ]: