Skip to content

switched_linear

Switched linear system benchmark.

switched_linear(a_on=None, a_off=None, threshold=0.0, initial_state=None)

Create a switched linear system benchmark.

Parameters:

Name Type Description Default
a_on ndarray | None

Dynamics matrix for the "on" location.

None
a_off ndarray | None

Dynamics matrix for the "off" location.

None
threshold float

Switching threshold on x[0].

0.0
initial_state ndarray | None

Optional initial state.

None

Returns:

Type Description
HybridSystem

HybridSystem configured with switching linear dynamics.

Source code in src/flowcean/hybrid/benchmarks/switched_linear.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def switched_linear(
    a_on: np.ndarray | None = None,
    a_off: np.ndarray | None = None,
    threshold: float = 0.0,
    initial_state: np.ndarray | None = None,
) -> HybridSystem:
    """Create a switched linear system benchmark.

    Args:
        a_on: Dynamics matrix for the "on" location.
        a_off: Dynamics matrix for the "off" location.
        threshold: Switching threshold on x[0].
        initial_state: Optional initial state.

    Returns:
        HybridSystem configured with switching linear dynamics.
    """
    if a_on is None:
        a_on = np.array([[-0.5, 2.0], [-2.0, -0.5]], dtype=float)
    if a_off is None:
        a_off = np.array([[-0.2, 1.0], [-1.0, -0.2]], dtype=float)

    def flow_on(
        _t: float,
        state: np.ndarray,
        _params: Parameters,
        _input_stream: InputStream,
    ) -> np.ndarray:
        return a_on @ state

    def flow_off(
        _t: float,
        state: np.ndarray,
        _params: Parameters,
        _input_stream: InputStream,
    ) -> np.ndarray:
        return a_off @ state

    def event_surface_to_off(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> float:
        return state[0] - params["threshold"]

    def event_surface_to_on(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> float:
        return state[0] - params["threshold"]

    on_dynamics = ContinuousDynamics(flow_on, label="on")
    off_dynamics = ContinuousDynamics(flow_off, label="off")
    mode_on = Location(on_dynamics, label="on")
    mode_off = Location(off_dynamics, label="off")

    to_off = Transition(
        source=mode_on,
        target=mode_off,
        event=EventSurface(
            event_surface_to_off,
            direction=CrossingDirection.FALLING,
            label="x_below",
        ),
    )
    to_on = Transition(
        source=mode_off,
        target=mode_on,
        event=EventSurface(
            event_surface_to_on,
            direction=CrossingDirection.RISING,
            label="x_above",
        ),
    )

    if initial_state is None:
        initial_state = np.array([1.0, 0.0], dtype=float)

    return HybridSystem(
        locations=[mode_on, mode_off],
        transitions=[to_off, to_on],
        initial_location=mode_on,
        initial_state=initial_state,
        parameters={"threshold": threshold},
    )