使用 PyTransitions 的非确定性状态机?

问题描述

我正在使用 pytransitions 并且遇到了需要有几个与其他状态无关的状态,并且使用 non deterministic state machine 进行建模非常有意义,这在数学上是等效的。

我想要类似以下的内容

from transitions import Machine
from transitions import EventData


class Matter(object):
    def __init__(self):
        transitions1 = [
            {'trigger': 'heat','source': 'solid','dest': 'liquid'},{'trigger': 'heat','source': 'liquid','dest': 'gas'},{'trigger': 'cool','source': 'gas','dest': 'solid'}
        ]

        transitions2 = [
            {'trigger': 'turn_on','source': 'off','dest': 'on'},{'trigger': 'turn_off','source': 'on','dest': 'off'},]
        self.machine = Machine(
                model=self,states=[['solid','liquid','gas'],['on','off']],transitions=[transitions1,transitions2],initial=['solid','off'],send_event=True
        )

    def on_enter_gas(self,event: EventData):
        print(f"entering gas from {event.transition.source}")

    def on_enter_liquid(self,event: EventData):
        print(f"entering liquid from {event.transition.source}")

    def on_enter_solid(self,event: EventData):
        print(f"entering solid from {event.transition.source}")

    def on_enter_on(self,event: EventData):
        print(f"entering on from {event.transition.source}")

    def on_enter_off(self,event: EventData):
        print(f"entering off from {event.transition.source}")

我可以定义一组新的状态为 states=itertools.product(states1,states2),然后定义所有的转换,如等价定理所示。

我想知道库中是否支持这种行为,如果支持,如何实现。

我有不止 2 组(大部分)独立的状态。真的,我有一堆开关,偶尔会有交互,但大部分都是独立的。

解决方法

... 具有多个与其他状态无关的状态,并且使用非确定性状态机进行建模很有意义

对我来说,这听起来像是您要寻找的不一定是非确定性的,而是 hierarchical/compound 状态和 concurrency/parallelism

您可以使用具有并发功能的转换 Hierarchical State Machine 扩展:

from transitions.extensions import HierarchicalMachine

states1 = ['solid','liquid','gas']
states2 = ['on','off']

transitions1 = [
    {'trigger': 'heat','source': 'solid','dest': 'liquid'},{'trigger': 'heat','source': 'liquid','dest': 'gas'},{'trigger': 'cool','source': 'gas','dest': 'solid'}
]

transitions2 = [
    {'trigger': 'turn_on','source': 'off','dest': 'on'},{'trigger': 'turn_off','source': 'on','dest': 'off'},]

combined_states = [
    {"name": "running","parallel":
        [
            dict(name="component1",states=states1,transitions=transitions1,initial=states1[0]),dict(name="component2",states=states2,transitions=transitions2,initial=states2[0])
        ]
    }
]

m = HierarchicalMachine(states=combined_states,auto_transitions=False,initial="running")
print(m.state)  # >>> ['running_component1_solid','running_component2_on']
m.turn_off()
print(m.state)  # >>> ['running_component1_solid','running_component2_off']

然而,HSM 比简单的 Machines 复杂得多。该文档提到了一些考虑到命名约定和需要遵循的嵌套/初始化配置的限制。

这就是为什么我通常会尝试为我的 FSM 架构找到最简单的解决方案。现在您的嵌套相当平坦,也可以通过一组模型和 Machines 来实现。转换的“规则手册”方法使得只需一台机器及其“调度”方法即可轻松管理处于不同状态的多个模型:

from transitions import Machine


class Model:
    pass


class MultiMachine(Machine):

    def __init__(self,configurations):
        # Initialize the machine blank,no states,no transitions and
        # no initial states. Disable auto_transitions since there shouldn't
        # be the possibility to transition e.g. from 'on' to 'liquid'.
        # Furthermore,ignore_invalid_triggers so that events not considered
        # by a model will not throw an exception.
        super().__init__(model=None,states=[],transitions=[],initial=None,ignore_invalid_triggers=True)
        # create a model for each configuration
        for states,transitions,initial in configurations:
            self.add_states(states)
            self.add_transitions(transitions)
            self.add_model(Model(),initial=initial)

    @property
    def state(self):
        return [model.state for model in self.models]


m = MultiMachine([(states1,transitions1,'solid'),(states2,transitions2,'off')])
assert m.state == ['solid','off']
m.dispatch("turn_on")
assert m.state == ['solid','on']
m.dispatch("heat")
assert m.state == ['liquid','on']

来自您的评论:

如何根据另一台子机的状态在一台子机中添加条件转换?例如,加热应该只有在打开的情况下才能使固体变成气体? [...] HSM,也许在这种情况下更好。

这可以通过 HSM 解决,方法是仅在源状态 heat 上定义 on_* 事件。但是,如果您有许多这些因变量,则嵌套可能会变得非常复杂。相反,您可以将对其他机器的 is_<state> 便利函数的引用添加到所有相关转换的条件列表中。这可以在初始化后完成,以防引导出现问题:

from transitions import Machine
from transitions.core import Condition

states1 = ['solid','gas']
states2 = ['off','on']

m1 = Machine(states=states1,initial=states1[0],transitions=[{'trigger': 'heat','dest': 'solid'}])
m2 = Machine(states=states2,initial=states2[0],transitions=[{'trigger': 'turn_on','dest': 'off'}])

# get all heat transitions and add the condition that they may only be valid when m2.is_on returns True
for trans in m1.get_transitions("heat"):
    trans.conditions.append(Condition(func=m2.is_on))
    # if you want to add an 'unless' statement pass `target=False`
    # to the condition. e.g. "heat unless m2 is off"
    # trans.conditions.append(Condition(func=m2.is_off,target=False))

assert m1.is_solid()
assert m2.is_off()
assert not m1.heat()
assert m1.is_solid()
assert m2.turn_on()
assert m1.heat()
assert m1.is_liquid()

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...