Hello World

We begin with a simple cogent that prints and then stops…

 1"""
 2Hello World!
 3"""
 4
 5from cognition import Cogent, DecisionProcess, IOContainer, Operator
 6
 7# ===
 8
 9# dp state is boolean, starting as False
10my_cogent = Cogent(DecisionProcess(lambda: False))
11
12
13@my_cogent.dp.operator(
14    "hello", terminal=True
15)  # shortcut to create + add an operator instance
16#    (named "hello" with the terminal flag to stop the DP)
17class HelloOperator(Operator[bool]):
18    """
19    Says hello!
20    """
21
22    def can_perform(self, state: bool, _io: IOContainer) -> bool:
23        # this will only apply if the state is False
24        return not state
25
26    def perform(self, _state: bool, _io: IOContainer) -> bool:
27        print("Hello, World!")
28
29        # mutable state can be just changed;
30        # returned values replace state
31        return True
32
33
34# initial state
35print(f"{my_cogent.dp.state}")
36
37# nifty way to run the cogent 🤓
38# 0. no arguments/sensors/actuators, so just starts the DP
39# 1. elaborate: no elaborators, done!
40# 2. terminal: no checks; no prior action selected; done!
41# 3. propose: hello (via can_perform, where state=False) is proposed!
42# 4. rank: only one action, so hello is selected
43# 5. apply: print, new state applied (via perform, where state=False)
44# 6. elaborate: no elaborators, done!
45# 7. terminal: no checks; prior action was terminal -> stop!
46# 8. by default, cogent doesn't continue after dp -> done!
47my_cogent()
48
49# final state
50print(f"{my_cogent.dp.state}")

Low-Level

For those interested in the details of how this is equivalently implemented using core primitives…

 1"""
 2Hello World!
 3(at a low level)
 4"""
 5
 6from collections.abc import Iterable
 7
 8from cognition import Action, BaseDecisionProcess, IOContainer, stringify
 9
10# ===
11
12
13@stringify("hello")
14def hello_factory(s: bool, _io: IOContainer) -> Iterable[Action[bool]]:
15    """action factory"""
16
17    @stringify("hello")
18    def hello(_a_s: bool, _a_io: IOContainer) -> bool:
19        """potentially proposed action"""
20
21        # if selected, say hello!
22        print("Hello, World!")
23
24        # return new state
25        return True
26
27    # if init state, propose hello!
28    if not s:
29        return (hello,)
30
31    # otherwise, nothing to propose
32    return ()
33
34
35# ===
36
37dp = BaseDecisionProcess(lambda: False)
38
39dp.add_action_factory(hello_factory)
40
41# done if state is True
42dp.add_termination_check(stringify("done?")(lambda s, _io: s))
43
44# ===
45
46# initial state
47print(f"{dp.state}")
48
49dp.run_until_done()
50
51# uncomment to see the result of stringify'd components
52# print(dp)
53
54# final state
55print(f"{dp.state}")

Notes…

  • The BaseDecisionProcess utilizes ActionFactory to identify what Action(s) are appropriate in the current context.

    • Notice how an Operator facilitates a clean separation of proposal vs application logic: Operator provides a predicate (can_perform) to control proposal of a singular Action instantiation, OperatorGenerator provides flexibility (via generate) to dynamically propose multiple instantiation(s).

  • The terminal flag of an instantiated Operator avoids duplicating the logic of Action post-conditions within a custom TerminationCheck.

  • Without any sensors/actuators, the Cogent serves largely as a wrapper around the supplied DP.

  • While not necessary for function, the stringify decorator provides objects a user-friendly str() result.

    • This is particularly helpful when examining a DP (via print).