Architecture
Synapsys is built in six layers. Each layer has a single responsibility and depends only on the layers below. This means you can use the mathematical core alone without touching the agent infrastructure, or swap the transport without changing any control logic.
Layer diagram
Design decisions
API / math engine separation
The synapsys.api layer is a convenience wrapper only. All mathematics lives in synapsys.core. This allows the API to evolve without touching the numerical core.
LTI class operator overloading
G1 * G2 (series), G1 + G2 (parallel), and G.feedback() allow composing systems with natural block algebra:
T = (C * G).feedback() # closed loop: C in series with G
Strategy pattern in transport
PlantAgent and ControllerAgent do not know how data is sent. They call transport.write() and transport.read(). The concrete implementation is injected at construction time — no control logic changes needed.
Transport lifecycle
The transport is owned by the caller, not the agent. The agent never calls transport.close(). This prevents double-free when multiple agents share views of the same memory block.
Continuous vs discrete
StateSpace(A, B, C, D, dt=0) is continuous. dt > 0 is discrete. The same class supports both:
is_stable()usesRe(poles) < 0for continuous and|poles| < 1for discretestep()delegates toscipy.signal.steporscipy.signal.dstepautomaticallyevolve(x, u)executesx(k+1) = Ax + Bustep by step for real-time simulation