Spaces:
Running on Zero
Running on Zero
File size: 3,892 Bytes
9936912 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 105 | """Robust control and uncertain system tools."""
from __future__ import annotations
from typing import Any
import numpy as np
from controlai_agent.registry import registry
@registry.register(
name="kharitonov_stability_test",
description="Evaluate robust Hurwitz stability of a real interval polynomial using Kharitonov's Theorem (tests the 4 extreme polynomials).",
parameters_schema={
"type": "object",
"properties": {
"lower_bounds": {
"type": "array",
"items": {"type": "number"},
"description": "Lower coefficient bounds [a0^-, a1^-, a2^-, a3^-] in ascending powers",
},
"upper_bounds": {
"type": "array",
"items": {"type": "number"},
"description": "Upper coefficient bounds [a0^+, a1^+, a2^+, a3^+] in ascending powers",
},
},
"required": ["lower_bounds", "upper_bounds"],
},
)
def kharitonov_stability_test(lower_bounds: list[float], upper_bounds: list[float]) -> dict[str, Any]:
lows = np.array(lower_bounds, dtype=float)
highs = np.array(upper_bounds, dtype=float)
polys = [
[lows[0], lows[1], highs[2], highs[3]],
[highs[0], highs[1], lows[2], lows[3]],
[highs[0], lows[1], lows[2], highs[3]],
[lows[0], highs[1], highs[2], lows[3]],
]
pole_sets = []
stable_flags = []
for poly in polys:
desc_poly = poly[::-1] # descending for np.roots
roots = np.roots(desc_poly)
pole_sets.append([[float(r.real), float(r.imag)] for r in roots])
stable_flags.append(bool(np.all(np.real(roots) < 0)))
robustly_stable = all(stable_flags)
return {
"kharitonov_polynomials_ascending": polys,
"stable_flags": stable_flags,
"is_robustly_hurwitz": robustly_stable,
"poles": pole_sets,
}
@registry.register(
name="small_gain_test",
description="Apply the Small Gain Theorem: certifies closed-loop input-output stability if induced norm product ||G1|| * ||G2|| < 1.",
parameters_schema={
"type": "object",
"properties": {
"gamma1": {"type": "number", "minimum": 0.0, "description": "H-infinity or induced norm bound of operator G1"},
"gamma2": {"type": "number", "minimum": 0.0, "description": "H-infinity or induced norm bound of operator G2"},
},
"required": ["gamma1", "gamma2"],
},
)
def small_gain_test(gamma1: float, gamma2: float) -> dict[str, Any]:
product = gamma1 * gamma2
certified = bool(product < 1.0)
return {
"norm_product": product,
"is_stability_certified": certified,
"stability_margin": 1.0 - product if certified else 0.0,
}
@registry.register(
name="multiplicative_uncertainty_test",
description="Check robust stability condition max |W(jw) * T(jw)| < 1 for output multiplicative plant uncertainty over frequency samples.",
parameters_schema={
"type": "object",
"properties": {
"weights": {"type": "array", "items": {"type": "number"}, "description": "Multiplicative weight |W(jw)| samples"},
"complementary_sensitivity": {"type": "array", "items": {"type": "number"}, "description": "Nominal |T(jw)| samples"},
},
"required": ["weights", "complementary_sensitivity"],
},
)
def multiplicative_uncertainty_test(
weights: list[float], complementary_sensitivity: list[float]
) -> dict[str, Any]:
w_arr = np.array(weights, dtype=float)
t_arr = np.array(complementary_sensitivity, dtype=float)
products = w_arr * t_arr
peak = float(np.max(products))
is_satisfied = bool(peak < 1.0)
return {
"pointwise_products": products.tolist(),
"peak_weighted_product": peak,
"is_robustly_stable_on_grid": is_satisfied,
}
|