Analysis¶
Linearization of a System about an operating point: the Jacobians (A, B, C, D) and an LTISystem.
- minilink.analysis.linearization.linearize_matrices(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, method='auto', eps=1e-06)[source]¶
Linearize
sysabout(x_bar, u_bar)and returnA, B, C, D.- Parameters:
sys (DynamicSystem) – Leaf or diagram whose
fdefines the dynamics.x_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
u_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
t (float, optional) – Time at which the Jacobians are evaluated.
params (dict, optional) – Parameter set; default the live
sys.params.of (selector or list of selectors, optional) – Rows of
CandD: an output port id, a diagram wire"block:port", or(selector, index)for one component. Default: every boundary output of a diagram, theyport of a leaf, or the full state (C = I) when there is noyport.wrt (selector or list of selectors, optional) – Columns of
BandD: an input port id, a diagram wire, or(selector, index). Default: every input port, stacked.method ({"auto", "fd", "jax"}, optional) – Differentiation backend, see
jacobian().eps (float, optional) – Central-difference step.
- Return type:
tuple[ndarray, ndarray, ndarray, ndarray]
- minilink.analysis.linearization.linearize(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, method='auto', eps=1e-06)[source]¶
Linearize
sysabout(x_bar, u_bar)and return anLTISystem.Same arguments as
linearize_matrices();x_bar=Nonedefaults to the nominal state like every tool of the analysis family. The model carries the operating point it was taken at (xbar,ubarfor the selected inputs,ybarfor the selected outputs), so a design made on it (lqr,kalman) knows where it applies.
- minilink.analysis.linearization.design_model(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, method='auto', eps=1e-06, measured=False)[source]¶
The linear model a design verb reads:
sysitself when it is anLTISystem, elsesyslinearized about its command port.An
LTISystemcarries the operating point it was taken at, so a point given here is refused: linearize the plant atx_barinstead. Any other system is linearized at(x_bar, u_bar), the nominal state and inputs by default, withBover the command port alone (u, else the one input that is not a disturbancedor a noisen; every other input is held at its nominal value) andCover the measured outputyalone, so a plant with noise ports gives a gain over its command and a box exposing its state is measured throughy.measured=True(the observer designs) refuses a plant withoutyrather than reading its state as the measurement.
Jacobians of a System at an operating point.
- minilink.analysis.derivatives.jacobian(sys, of, wrt, x_bar=None, u_bar=None, t=0.0, params=None, *, method='auto', eps=1e-06)[source]¶
Return
d(of)/d(wrt)ofsysat(x_bar, u_bar, t, params).jacobian(sys, "f", "x")is ∂f/∂x: the first argument names what is differentiated ("f","step", an output port, or a diagram wire"block:port"), the second the variable ("x","u", an input port,"t","params", or a wire). The point follows in the order off(x, u, t, params)with the textbook defaults, and each call compiles its own evaluator (about a millisecond): exact under JAX when the system traces, central finite differences otherwise. Nothing is stored on the system; keepevaluator.jacobian(of, wrt)when a loop needs the callable.- Parameters:
sys (System) – Leaf, diagram, static block, or step system.
of (str) –
"f"("step"on step systems), an output port id, or a diagram wire"block:port".wrt (str) –
"x","u"(every input stacked), an input port id,"t","params", or a diagram wire (additive perturbation of that signal).x_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
u_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
t (float, optional) – Time (the step index
kon step systems).params (dict, optional) – Parameter set; default the live
sys.params.method ({"auto", "fd", "jax"}, optional) –
"auto"is exact under JAX when it is installed and the system traces, finite differences otherwise;"jax"raises instead of falling back.eps (float, optional) – Central-difference step.
- Returns:
Shape
(dim(of), dim(wrt));(dim(of),)forwrt="t"; forwrt="params"a dict shaped likeparamswith leaves(dim(of), *leaf.shape)(float leaves only). A block whosefreads instance attributes instead of itsparamsargument reports zero sensitivity, like every parametric tier.- Return type:
ndarray or dict
Discretization: a continuous-time system as a discrete-time step model.
- minilink.analysis.discretization.discretize(system, dt=None, *, integrator='rk4', params=None)[source]¶
Wrap a
DynamicSystemas aStepSystemwith hold intervaldt.x_{k+1} = step(x, u, k; p)integratesfover[k dt, (k + 1) dt]withintegrator"rk4"or"euler"(the same word asSys2Gym), the input held.pdefaults to the wrapper’sparams: the source’s liveparamswhenparamsisNone, else the dict given, which replaces them (RULES 4.4). The hold interval has one owner, the wrapper’sdt, and never reachesforh: whendtis omitted it is read once fromparams["dt"], and a"dt"key is dropped from theparamsgiven. Three cases are therefore refused:dtand aparams["dt"]that differ, since one would be dropped unseen; aparamsthat holds only"dt"while the source has other params, since it would replace them with{}(passdt=to keep them); and, whenparamsisNone, a source whose own params carry"dt", since they reachflive besidedt(remove the key and passdt=). The wrapper starts from the source’sx0and keeps its state, input andymetadata.- Parameters:
system (DynamicSystem)
dt (float | None)
integrator (str)
params (dict | None)
- Return type:
- class minilink.analysis.discretization.DiscretizedDynamicSystem(source, dt, params=None, *, integrator)[source]¶
Discrete-time wrapper over a continuous
DynamicSystem.The hold interval
dtlives on the wrapper and nowhere else:paramsare handed to the source’sfandhuntouched, so building the wrapper or assigningparamsrefuses a"dt"key in them (a key added later in place to the source’s live params is not checked). The wrapper starts from the source’sx0and copies its state, input andylabels, units, bounds and nominal values.- Parameters:
source (DynamicSystem)
dt (float)
params (dict | None)
integrator (str)
- property params: dict¶
the source’s live ones, or the override.
The source’s live
paramswhileparams_overrideisNone(the same dict, so an in-place edit reaches the source), else that dict. Assigningparamssetsparams_override, and refuses a"dt"key in the params that would reachf: the value, or the source’sparamswhen the value isNone.- Type:
The params
fandhreceive
- class minilink.analysis.discretization.DiscretizedEulerDynamicSystem(source, dt, params=None)[source]¶
x_{k+1} = x_k + dt f(x_k, u_k, t_k; p).- Parameters:
source (DynamicSystem)
dt (float)
params (dict | None)
- class minilink.analysis.discretization.DiscretizedRK4DynamicSystem(source, dt, params=None)[source]¶
One RK4 step of
fover[t_k, t_k + dt]with ZOH onu.- Parameters:
source (DynamicSystem)
dt (float)
params (dict | None)
Structural analysis: the controllability and observability of a system’s linear model.
- minilink.analysis.structural.controllability_matrix(A, B)[source]¶
Return the Kalman controllability matrix
𝒞 = [B, AB, …, Aⁿ⁻¹B]of the pair(A, B).The arrays twin of
controllability(), aslqr_gainislqr’s.
- minilink.analysis.structural.observability_matrix(A, C)[source]¶
Return the Kalman observability matrix
𝒪 = [C; CA; …; CAⁿ⁻¹]of the pair(A, C).The arrays twin of
observability().
- minilink.analysis.structural.controllability(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, tol=1e-09, method='auto', eps=1e-06)[source]¶
Return the controllability test of
sys’s linear model.The pair
(A, B)is controllable when its Kalman controllability matrix has full row rankn.sysis anLTISystem, read as is, or any plant, linearized at(x_bar, u_bar)about its command port alone (the nominal point by default), aslqrandplaceread it.tolis the relative singular-value threshold below which a direction counts as missing, the same asminreal()’s. On arrays,controllability_matrix()gives𝒞.
- minilink.analysis.structural.observability(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, tol=1e-09, method='auto', eps=1e-06)[source]¶
Return the observability test of
sys’s linear model.The pair
(A, C)is observable when its Kalman observability matrix has full column rankn.sysis anLTISystem, read as is, or any plant, linearized at(x_bar, u_bar)withCover its measured outputy(a plant withoutyis refused), asluenbergerandkalmanread it;tolas incontrollability(). On arrays,observability_matrix()gives𝒪.
- class minilink.analysis.structural.StructuralResult(matrix, rank, n)[source]¶
Result of a controllability or observability test.
- Parameters:
matrix (ndarray)
rank (int)
n (int)
- property is_full_rank: bool¶
the Kalman matrix has rank
n.- Type:
The test passes
Equilibria of DynamicSystem models.
- minilink.analysis.equilibria.find_equilibrium(sys, x_guess=None, u_bar=None, t=0.0, params=None, *, tol=1e-09)[source]¶
Return a state
x_eqnearx_guesswithf(x_eq, u_bar, t) ≈ 0.- Parameters:
sys (DynamicSystem) – Continuous-time model whose
fdefines the dynamics (leaf subclass or wired diagram with stackedf).x_guess (array of shape (n,), optional) – Initial guess for the equilibrium state. Defaults to the nominal state.
u_bar (array of shape (m,), optional) – Held input. Defaults to the system’s nominal port values.
t (float, optional) – Time at which to evaluate
f.params (dict, optional) – Parameter set forwarded to
f.tol (float, optional) – The solver’s step tolerance (
xtol); the root is accepted when‖f‖ ≤ 10³ tol.
- Returns:
x_eq – The equilibrium state.
- Return type:
np.ndarray
- Raises:
RuntimeError – If the solver does not drive
‖f‖to10³ tolor below.
Modal analysis: the poles and mode shapes of the linearized dynamics, and their animation.
- minilink.analysis.modal.modal_analysis(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, method='auto', eps=1e-06)[source]¶
Linearize about
(x_bar, u_bar)and eigendecomposeA.- Parameters:
sys (System) – Plant or other system with
f(andh).x_bar (array of shape (n,), optional) – Operating-point state. Defaults to the nominal state.
u_bar (array of shape (m,), optional) – Operating-point input. Defaults to port nominals.
t (float, optional) – Time at which the Jacobian is evaluated.
params (dict, optional) – Parameter set; default the live
sys.params.method ({"auto", "fd", "jax"}) – Differentiation backend, see
jacobian().eps (float, optional) – Central-difference step.
- Returns:
poles (ndarray of shape (n,)) – Eigenvalues of
A.modes (ndarray of shape (n, n)) – Eigenvectors (columns are mode shapes in perturbation coordinates).
- minilink.analysis.modal.animate_modal(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, mode='all', method='auto', eps=1e-06, amplitude=1.0, tf=None, n_steps=2001, time_factor_video=3.0, renderer='matplotlib', is_3d=False, show=True, html=None, native=True)[source]¶
Linearize, excite selected mode(s), and animate them on
sys.Each mode is animated as an absolute state, the operating point plus the mode’s motion, so the system’s own kinematics draw it. Calls
modal_analysis(), thenanimate()once per mode.- Parameters:
sys (System) – System used for graphics (usually the nonlinear plant).
x_bar (array of shape (n,), optional) – Linearization operating point. Defaults to the nominal state.
u_bar (array of shape (m,), optional) – Operating-point input during the animation.
mode (int or
'all') – Mode index to animate, or every index0 … n-1.
- Returns:
Same as
modal_analysis().- Return type:
poles, modes
Linear analysis of one channel’s matrices (A, B, C, D): poles, zeros, gain, minimal realization, responses, margins, root locus.
- minilink.analysis.linear.zeros(A, B, C, D)[source]¶
Transmission zeros of the square channel
(A, B, C, D): the finite eigenvalues of its Rosenbrock pencil.
- minilink.analysis.linear.gain(A, B, C, D)[source]¶
Leading coefficient
kof the factored transfer function of a SISO channel.dwhen the channel has feedthrough, otherwise the first nonzero Markov parameter; read off the response at one real point beyond every pole and zero.
- minilink.analysis.linear.frequency_response(A, B, C, D, w)[source]¶
Frequency response of a SISO channel, one complex value per frequency in
w.
- minilink.analysis.linear.frequency_range(A, B, C, D)[source]¶
(w_min, w_max)covering the dynamics and any 0 dB crossing.One decade beyond the slowest and fastest nonzero pole or zero, then widened until the band brackets
|G| = 1. The widening matters: roots at the origin carry no rate, so an integrator’s crossover sits below the pole-derived band, and a large static gain pushes it above — in both cases a band read from the roots alone misses the crossover andmargins()would report an infinite margin.
- minilink.analysis.linear.minreal(A, B, C, D, *, tol=1e-09)[source]¶
Minimal realization: drop the modes that are uncontrollable or unobservable.
The pole/zero pairs of those modes cancel;
pzmap,root_locusandtransfer_functionrun it by default.tolis the relative singular-value threshold below which a direction counts as missing, the onecontrollability()andobservability()use. A realization that is already minimal comes back in its own coordinates.
- class minilink.analysis.linear.Margins(gain_margin_db, phase_margin_deg, w_gain_crossover, w_phase_crossover)[source]¶
Gain and phase margins of a loop transfer function
L(jw).- Parameters:
gain_margin_db (float)
phase_margin_deg (float)
w_gain_crossover (float)
w_phase_crossover (float)
- minilink.analysis.linear.margins(w, G, response=None)[source]¶
Margins from a sampled response
G = L(jw); crossings found by interpolation.With
response, a callablew -> L(jw), each bracketed crossing is solved exactly on it (Brent’s method), so the margins do not depend on the grid, which only has to bracket them.
- minilink.analysis.linear.closed_loop_poles(A, B, C, D, K)[source]¶
Poles of the SISO loop closed with
u = -K y.
- minilink.analysis.linear.root_locus(A, B, C, D, gains=None, *, n_gains=400)[source]¶
Closed-loop poles over a gain sweep, one continuous branch per column.
gainsdefaults to0followed by six log-spaced decades ending where the far branches reach ten times the radius of the open-loop poles and zeros (n_gainspoints); consecutive gain points are refined while any branch moves by more than two percent of that radius. Returns(gains, roots)withrootsof shape(len(gains), n_states).
- minilink.analysis.linear.step_response(A, B, C, D, t)[source]¶
Unit-step response from rest, exact on a uniform time grid
t.One matrix exponential gives the zero-order-hold pair
(A_d, B_d); the state is then marched exactly.
- minilink.analysis.linear.settling_horizon(A)[source]¶
Default step-response horizon: eight times the slowest stable time constant.
The Riccati equations of linear-quadratic design: the algebraic solution and the exact step of the differential one.
- minilink.analysis.riccati.care(A, B, Q, R)[source]¶
Return
P, the stabilizing solution of the continuous algebraic Riccati equation.
- minilink.analysis.riccati.hamiltonian_matrix(A, B, Q, R_inv)[source]¶
The Hamiltonian matrix: with the co-state
λ = S x,z = [x; λ]followsż = H z.
- minilink.analysis.riccati.riccati_transition(A, B, Q, R_inv, dt)[source]¶
The transition
Eof the Hamiltonian system over one step ofdtbackward in time.
- minilink.analysis.riccati.riccati_step(A, B, Q, R_inv, S, dt)[source]¶
S(t) → S(t − dt)with the dynamics frozen over the interval, exactly.The same step on the dual pair
(Aᵀ, Cᵀ)carries a filter covariance fromP(t)forward toP(t + dt): the filter equation is the control equation with time reversed and the matrices transposed.
- minilink.analysis.riccati.riccati_map(E, S)[source]¶
One exact step
S(t) → S(t − dt)of the Riccati equation, from the transitionE.
Step response of one input–output channel of the linear model, and its textbook figures.
- class minilink.analysis.time_response.StepInfo(rise_time, settling_time, overshoot, peak, peak_time, steady_state)[source]¶
Textbook step-response figures (MATLAB
stepinfoconventions).- Parameters:
rise_time (float)
settling_time (float)
overshoot (float)
peak (float)
peak_time (float)
steady_state (float)
- minilink.analysis.time_response.step_response(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, tf=None, n=500, method='auto', eps=1e-06)[source]¶
Return
(time, y): the unit-step response of the selected channel from rest.The response of the linear model, in deviation variables:
sysas is when it is anLTISystem, else linearized at(x_bar, u_bar). Same channel arguments asbode();tfdefaults to eight times the slowest stable time constant, andnsamples span[0, tf]. The step of the nonlinear loop itself is a simulation,closed_loop(C, plant, reference=Step(...))thencompute_trajectory, andstep_info()reads its sampled output the same way.- Parameters:
n (int)
method (str)
eps (float)
- Return type:
tuple[ndarray, ndarray]
- minilink.analysis.time_response.step_info(time, y)[source]¶
Rise time, settling time, overshoot, peak and steady state of a sampled step response.
Reads any sampled response: the linear one from
step_response(), or a simulated trajectory’s time and output arrays. The settling band is 2 % of the response’s peak deviation from its final value: 2 % of the final value for a step from rest that neither dips below its start nor overshoots by 100 %, and still defined for a response that returns to zero.- Return type:
- minilink.analysis.time_response.plot_step_response(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, tf=None, n=500, method='auto', eps=1e-06, backend='matplotlib', show=True)[source]¶
Step response of the selected channel with rise time, settling time and overshoot.
The linear model’s response, as
step_response(); the title reads “Linear Step Response” for anLTISystemand “Linearized Step Response” for a system linearized here.- Parameters:
n (int)
method (str)
eps (float)
show (bool)
- Return type:
PlotResult
Frequency-domain analysis of one input–output channel of a linearized model.
- minilink.analysis.frequency.frequency_response(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=200, method='auto', eps=1e-06)[source]¶
Return
(w, G)withG(jw)complex on the selected channel.- Parameters:
sys (System) – System or diagram to linearize before computing the frequency response.
x_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
u_bar (array-like, optional) – Operating point; default the nominal state and the nominal port values.
t (float, optional) – Time at which the Jacobians are evaluated.
params (dict, optional) – Parameter set; default the live
sys.params.of (str or (str, int), optional) – Output of the channel: a port id, a diagram wire
"block:port", or(selector, index)for one component (a bare id means component 0). Default: component 0 of the primary output.wrt (str or (str, int), optional) – Input of the channel, same forms. Default: component 0 of the first input port.
w (array, optional) – Frequencies in rad/s. When omitted, a logarithmic grid runs one decade below the slowest pole or zero to one decade above the fastest, widened until it brackets the 0 dB crossing.
n (int, optional) – Number of frequencies for the automatic grid.
method ({"auto", "fd", "jax"}, optional) – Differentiation backend, see
jacobian().eps (float, optional) – Central-difference step.
- Return type:
tuple[ndarray, ndarray]
- minilink.analysis.frequency.bode(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=200, method='auto', eps=1e-06)[source]¶
Return the SISO Bode response
(w, magnitude_db, phase_deg).Same arguments as
frequency_response(); the phase is unwrapped.- Parameters:
n (int)
method (str)
eps (float)
- Return type:
tuple[ndarray, ndarray, ndarray]
- minilink.analysis.frequency.margins(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=2000, method='auto', eps=1e-06)[source]¶
Gain and phase margins of the selected channel taken as a loop gain.
Same arguments as
frequency_response(); returnsMargins(infwhere a crossover does not exist).- Parameters:
n (int)
method (str)
eps (float)
- Return type:
- minilink.analysis.frequency.nyquist(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=500, method='auto', eps=1e-06)[source]¶
Return
(w, G)for the Nyquist contour, positive frequencies only.Same arguments as
frequency_response(). The plot mirrors the conjugate branch; poles on the imaginary axis make|G|blow up at those frequencies (the indented contour is not drawn).- Parameters:
n (int)
method (str)
eps (float)
- Return type:
tuple[ndarray, ndarray]
- minilink.analysis.frequency.pzmap(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, method='auto', eps=1e-06, minimal=None)[source]¶
Return the selected SISO channel as
(zeros, poles, gain).Same arguments as
frequency_response()without the frequency grid: poles are the eigenvalues ofA, zeros the transmission zeros of the channel,gainthe leading coefficient of its factored transfer function.minimal(default: yes, with a one-time notice when a pair cancels) first reduces the channel to its minimal realization, so the uncontrollable and unobservable modes cancel;minimal=Falsekeeps every mode of the realization.- Parameters:
method (str)
eps (float)
minimal (bool | None)
- Return type:
tuple[ndarray, ndarray, float]
- minilink.analysis.frequency.root_locus(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, gains=None, method='auto', eps=1e-06, minimal=None)[source]¶
Closed-loop poles of
u = -K yon the selected channel over a gain sweep.Same arguments as
frequency_response()without the frequency grid;gainsdefaults to an adaptive sweep fromK = 0to the gain where the far branches leave the picture. Returns(gains, roots)withrootsof shape(len(gains), n), one continuous branch per column.minimalas inpzmap().- Parameters:
method (str)
eps (float)
minimal (bool | None)
- Return type:
tuple[ndarray, ndarray]
- minilink.analysis.frequency.transfer_function(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, method='auto', eps=1e-06, minimal=None)[source]¶
Return the selected SISO channel as a
TransferFunctionblock.Same arguments as
frequency_response()without the frequency grid;minimalas inpzmap(). The numerator and denominator come from the zeros, poles and gain ofpzmap(); the block is their state-space realization and carriesnumerator,denominator,polesandzeros, so it can be plotted, simulated, or wired like any other block.- Parameters:
method (str)
eps (float)
minimal (bool | None)
- minilink.analysis.frequency.plot_bode(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=200, margins=True, method='auto', eps=1e-06, backend='matplotlib', show=True, title=None)[source]¶
Bode diagram of the selected channel;
margins=Truemarks the crossovers.The crossovers and the printed margins are those of
margins(), read on its grid (or onw), not on thenplotted frequencies.titleoverrides the figure heading (default"Bode Diagram").- Parameters:
n (int)
margins (bool)
method (str)
eps (float)
show (bool)
title (str | None)
- Return type:
PlotResult
- minilink.analysis.frequency.plot_pzmap(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, method='auto', eps=1e-06, minimal=None, radius=None, backend='matplotlib', show=True)[source]¶
Pole-zero map of the selected channel:
xpoles,ozeros.minimalas inpzmap().radiusframes the roots within that distance of the origin, the window a stiff model needs to show its slow roots; the default frames every root.- Parameters:
method (str)
eps (float)
minimal (bool | None)
radius (float | None)
show (bool)
- Return type:
PlotResult
- minilink.analysis.frequency.plot_root_locus(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, gains=None, method='auto', eps=1e-06, minimal=None, radius=None, backend='matplotlib', show=True)[source]¶
Root locus of the selected channel closed with
u = -K y.minimalas inpzmap().radiusframes the branches within that distance of the origin; the default frames three times the radius of the open-loop roots, so the far tails of the asymptotes leave the picture.- Parameters:
method (str)
eps (float)
minimal (bool | None)
radius (float | None)
show (bool)
- Return type:
PlotResult
- minilink.analysis.frequency.plot_nyquist(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, of=None, wrt=None, w=None, n=500, method='auto', eps=1e-06, backend='matplotlib', show=True)[source]¶
Nyquist diagram of the selected channel with the critical point
-1.- Parameters:
n (int)
method (str)
eps (float)
show (bool)
- Return type:
PlotResult
Sensitivity functions of a feedback loop, built from its plant, controller and filter.
- minilink.analysis.sensitivity_functions.sensitivity(*, plant, controller, filter=None, x_bar=None, u_bar=None, t=0.0, params=None, method='auto', eps=1e-06)[source]¶
Return the sensitivity function
Sof the loop as aTransferFunction.Smaps an output disturbance toyand the referencerto the errore. The loop is the plantH, the controllerCand the optional filterFon the measurement, each a SISOSystemlinearized first.- Parameters:
plant (System) – The plant
H, from its one input besidesdandnto its output.controller (System) – The controller
C, from the erroreto the commandu. A controller in the reference form (inputsrandy) is refused: pass its channel fromr,transfer_function(C, wrt="r").filter (System, optional) – The filter
Fon the measurement;F = 1when omitted.x_bar (optional) – The plant’s operating point, as in
bode(); the controller and the filter are taken at their nominal point.u_bar (optional) – The plant’s operating point, as in
bode(); the controller and the filter are taken at their nominal point.t (optional) – The plant’s operating point, as in
bode(); the controller and the filter are taken at their nominal point.params (optional) – The plant’s operating point, as in
bode(); the controller and the filter are taken at their nominal point.method ({"auto", "fd", "jax"}, optional) – Differentiation backend of the linearizations.
eps (float, optional) – Central-difference step.
- Returns:
S(s), ready forbode(),plot_bodeor a simulation; its ports are labelledrande(T:rtoy,PS:wtoy,CS:vtou).- Return type:
- minilink.analysis.sensitivity_functions.complementary_sensitivity(*, plant, controller, filter=None, x_bar=None, u_bar=None, t=0.0, params=None, method='auto', eps=1e-06)[source]¶
Return the complementary sensitivity
T, the map fromrtoy.Same arguments as
sensitivity(); without a filter,S + T = 1.- Parameters:
method (str)
eps (float)
- minilink.analysis.sensitivity_functions.load_sensitivity(*, plant, controller, filter=None, x_bar=None, u_bar=None, t=0.0, params=None, method='auto', eps=1e-06)[source]¶
Return the load sensitivity
PS, the map from a disturbancedat the plant input toy.Same arguments as
sensitivity().- Parameters:
method (str)
eps (float)
- minilink.analysis.sensitivity_functions.noise_sensitivity(*, plant, controller, filter=None, x_bar=None, u_bar=None, t=0.0, params=None, method='auto', eps=1e-06)[source]¶
Return the noise sensitivity
CS, the map from measurement noisento the commandu.Same arguments as
sensitivity(). In the loop,nreachesuas-CS: the sign of the negative feedback is left out.- Parameters:
method (str)
eps (float)
Region of attraction of a closed loop: a quadratic Lyapunov certificate V = (x − x̄)ᵀ P (x − x̄) ≤ c.
- minilink.analysis.lyapunov.METHODS = ('quadratic', 'sos')¶
Families of Lyapunov function this tool can build.
- minilink.analysis.lyapunov.region_of_attraction(sys, x_bar=None, u_bar=None, t=0.0, params=None, *, method='quadratic', Q=None, window=None, samples=None, search='auto')[source]¶
Certify a region of attraction of
sysaround an equilibrium.Psolves the Lyapunov equation at the equilibrium the tool finds; the levelcis the largest sampled sublevel set on whichV̇ < 0inside the state box, so it is a sharp estimate, not a proof, andverify()is its simulation counter-check.- Parameters:
sys (DynamicSystem) – Autonomous system, normally a closed loop such as
ctl @ plant.x_bar (array of shape (n,), optional) – Guess of the equilibrium; refined by
find_equilibrium(). Defaults to the nominal state. The refined point is what the certificate reports: a law tuned by learning often settles beside the state it was aimed at.u_bar (optional) – Held input, time, and parameter set, as in every analysis tool.
t (optional) – Held input, time, and parameter set, as in every analysis tool.
params (optional) – Held input, time, and parameter set, as in every analysis tool.
method ({"quadratic", "sos"}) – Family the Lyapunov function comes from.
"quadratic"solves the Lyapunov equation of the linearization, the textbook construction and the only one implemented;"sos"is reserved for a sum-of-squares certificate, which would also make the level a proof rather than an estimate.Q (array of shape (n, n), optional) – Right-hand side of the Lyapunov equation; the identity by default. A larger
Qin one direction weighs that direction more inV.window (BoxSet or (lower, upper), optional) – Box the sublevel search covers. The default searches twice, first on the natural scale of
Vand then on a box sized to that answer, so the samples land where the level is decided.samples (int, optional) – Sampling budget per pass (default
201**2in one or two states, 50 000 above).search ({"auto", "grid", "random"}) – How the sublevel set is sampled.
"grid"sweeps a regular grid, the default in one or two states and what the plots draw;"random"uses a low-discrepancy sequence, which scales better.
- Return type:
- Raises:
ValueError – If the linearization is not Hurwitz: an unstable equilibrium has no region of attraction to certify.
NotImplementedError – For
method="sos".
- class minilink.analysis.lyapunov.LyapunovCertificate(sys, x_bar, P, level, Q, poles, window, limiting_state, sample_spread=0.0, method='quadratic', u_bar=None, t=0.0, params=None, backend=None, evaluator=None)[source]¶
Lyapunov function and the level that certifies a region of attraction.
Returned by
region_of_attraction(). The certified region is{x : V(x) <= level}: every state in it converges tox_bar.- Parameters:
sys (object)
x_bar (ndarray)
P (ndarray)
level (float)
Q (ndarray)
poles (ndarray)
window (BoxSet)
limiting_state (ndarray)
sample_spread (float)
method (str)
u_bar (ndarray)
t (float)
params (object)
backend (str)
evaluator (object)
- sys¶
The closed loop the certificate belongs to.
- Type:
- x_bar¶
Equilibrium the region is centered on. It is found, not assumed, so it may differ from the operating point that was asked for.
- Type:
array of shape (n,)
- P¶
Lyapunov matrix of the quadratic family,
V(x) = (x - x_bar)ᵀ P (x - x_bar).- Type:
array of shape (n, n)
- level¶
Certified value
cofV.- Type:
float
- Q¶
Right-hand side of
AᵀP + PA = -Q.- Type:
array of shape (n, n)
- poles¶
Eigenvalues of the linearization, kept as evidence.
- Type:
array of shape (n,)
- limiting_state¶
Sampled state that pinned the level: the nearest place, in
V, where the loop stops making progress. Reading the control input there usually explains the certificate.- Type:
array of shape (n,)
- sample_spread¶
Relative disagreement between the levels two halves of the samples give. Near zero means the sweep had enough samples to decide; see
sample_limited.- Type:
float
- property extent: ndarray¶
Half-width of the certified region along each state.
- slice_extent(x_axis=0, y_axis=1)[source]¶
Half-widths of the region’s slice through the equilibrium in two states.
With the other states pinned at
x_barthe certified set cuts a smaller ellipse thanextent, which is its shadow on each axis. The slice is what a phase-plane plot draws.- Parameters:
x_axis (int)
y_axis (int)
- Return type:
ndarray
- property sample_limited: bool¶
Truewhen the level moved between two halves of the samples.The sublevel search is only as good as its coverage, and coverage thins out fast with the number of states. When this is
Truethe level is an optimistic guess rather than a sharp estimate: raisesamples, narrowwindow, or read it as indicative only.
- property rate: float¶
the natural time scale of the loop.
- Type:
Slowest linearized decay rate
- verify(n=200, tf=None, tol=None)[source]¶
Integrate
nstates drawn inside the certified region: all must converge.States are drawn uniformly in the ellipsoid
{V <= level}.tfdefaults to ten time constants of the slowest linearized mode andtolto a hundredth of the certified extent. A failure means the sublevel search was too coarse, or the model is not what it seems, and the report carries the offending state.- Parameters:
n (int)
tf (float | None)
tol (float | None)
- plot(x_axis=0, y_axis=1, *, basin=False, detail=True, verified=0, trajectories=(), n=201, limits=None, ax=None, show=True)[source]¶
Phase-plane slice of the certified region. See
plot_region_of_attraction().- Parameters:
x_axis (int)
y_axis (int)
basin (bool)
detail (bool)
verified (int)
n (int)
show (bool)
- class minilink.analysis.lyapunov.VerificationReport(converged, final_distance, counterexample=None)[source]¶
Simulation’s verdict on a certificate: every trial inside it must converge.
- Parameters:
converged (ndarray)
final_distance (ndarray)
counterexample (ndarray | None)
- property holds: bool¶
Truewhen every sampled state converged to the equilibrium.
- minilink.analysis.lyapunov.plot_region_of_attraction(certificate, x_axis=0, y_axis=1, *, basin=False, detail=True, verified=0, trajectories=(), n=201, limits=None, ax=None, show=True)[source]¶
Draw a slice of a certified region: the level set, and what pins it.
The two swept states are
x_axisandy_axis; the others are pinned at the equilibrium, so what is drawn is the slice of the certified set throughx_bar(smaller than its shadow on the two axes whenever the states are coupled).Layers, each in the legend:
basin=Trueshades the states of the slice that really converge, by simulation, which shows how much a quadraticVgives up.detail=Trueadds the curve whereVstops decreasing, and the sample that set the level when it lies on this slice — on a slice the two need not touch, since the binding state usually sits off the plane.verified=200draws the statesLyapunovCertificate.verify()would test, marked by whether they converged. They lie inside the certified set, because the claim is what is on trial and a state there that fails is a counterexample; whether states outside converge is whatbasinanswers. Two-state loops only, where the draw lies on the plane.trajectoriesis a sequence of initial states to overlay.limitsis an explicit((x_min, x_max), (y_min, y_max))window; the default frames the slices being drawn.- Parameters:
certificate (LyapunovCertificate)
x_axis (int)
y_axis (int)
basin (bool)
detail (bool)
verified (int)
n (int)
show (bool)
- minilink.analysis.lyapunov.EVEN = slice(0, None, 2)¶
The two disjoint halves the level search scores itself on.