Spaces:
Running on Zero
Running on Zero
| """Build ControlBench v1: Universal 50-Item Control Engineering Benchmark.""" | |
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| BENCHMARK_PATH = Path("benchmarks/controlbench_v1.jsonl") | |
| def build_controlbench() -> None: | |
| items = [] | |
| # ========================================================================= | |
| # PILLAR 1: THEORY & CORE CONCEPTS (10 items) | |
| # ========================================================================= | |
| items.append({ | |
| "id": "cb_theory_001", | |
| "pillar": "theory_and_concepts", | |
| "topic": "frequency_domain_heuristics", | |
| "prompt": "What is the theoretical and practical relationship between the open-loop Phase Margin (PM) and the closed-loop damping ratio (zeta) for a standard second-order system? Explain why this approximation holds and where it breaks down.", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "zeta approx PM / 100 (for PM in degrees <= 60 deg)", | |
| "Exact formula PM = arctan(2*zeta / sqrt(sqrt(1+4*zeta^4) - 2*zeta^2))", | |
| "Approximation holds accurately for 0 <= zeta <= 0.6", | |
| "Breaks down for high phase margins (PM > 70 deg) or systems with non-dominant high-frequency poles/zeros", | |
| ], | |
| "rubric": { | |
| "formula_correctness": 2.0, | |
| "range_of_validity": 1.5, | |
| "physical_intuition": 1.5, | |
| }, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_002", | |
| "pillar": "theory_and_concepts", | |
| "topic": "state_space_controllability", | |
| "prompt": "Compare the Controllability Matrix rank condition (Kalman test) with the Popov-Belevitch-Hautus (PBH) rank test. When is PBH preferred in engineering practice, and what additional modal information does PBH provide?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Kalman matrix C = [B, AB, ..., A^(n-1)B] can be numerically ill-conditioned for large n due to matrix powers", | |
| "PBH test checks rank([lambda*I - A, B]) = n for every eigenvalue lambda of A", | |
| "PBH directly identifies uncontrollable modes (eigenvalues where rank drops)", | |
| "PBH immediately determines stabilizability (if uncontrollable modes have Re(lambda) < 0)", | |
| ], | |
| "rubric": {"numerical_ill_conditioning": 2.0, "uncontrollable_mode_identification": 2.0, "stabilizability": 1.0}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_003", | |
| "pillar": "theory_and_concepts", | |
| "topic": "non_minimum_phase_systems", | |
| "prompt": "Why do Right-Half-Plane (RHP) zeros impose fundamental performance limitations in feedback control systems? Explain using the Poisson Integral formula or Bode sensitivity integral (waterbed effect).", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "RHP zeros cause initial undershoot in step response", | |
| "Bode sensitivity integral (waterbed effect): reduction of sensitivity |S(jw)| at low frequencies forces amplification elsewhere", | |
| "RHP zero at z requires closed-loop bandwidth to be bounded below approximately z/2", | |
| "Cannot invert RHP zeros without creating unstable closed-loop pole-zero cancelations", | |
| ], | |
| "rubric": {"undershoot_explanation": 1.5, "waterbed_effect": 2.0, "bandwidth_limitation": 1.5}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_004", | |
| "pillar": "theory_and_concepts", | |
| "topic": "lyapunov_stability", | |
| "prompt": "Explain the fundamental difference between Lyapunov's Indirect Method (linearization) and Lyapunov's Direct Method. What are the limitations of indirect linearization regarding asymptotic stability and domain of attraction?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Indirect method: linearizes Jacobian at equilibrium; Re(lambda) < 0 implies local asymptotic stability", | |
| "Indirect method fails / is inconclusive if any eigenvalue has Re(lambda) = 0 (critical cases)", | |
| "Indirect method provides no information on domain of attraction (valid only in an infinitesimal neighborhood)", | |
| "Direct method: constructs positive-definite V(x) with negative semi-definite or negative definite V_dot; valid globally or over an estimated invariant set", | |
| ], | |
| "rubric": {"indirect_definition": 1.5, "zero_real_part_limitation": 1.5, "domain_of_attraction": 2.0}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_005", | |
| "pillar": "theory_and_concepts", | |
| "topic": "separation_principle", | |
| "prompt": "State the Separation Principle for Linear-Quadratic-Gaussian (LQG) control. Under what conditions does it hold, and why does LQG lack the guaranteed robustness margins of deterministic LQR?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Separation principle: optimal controller decomposes into deterministic LQR state feedback K and optimal Kalman state observer L independently", | |
| "Closed-loop eigenvalues are exactly the union of LQR poles and observer poles", | |
| "Doyle (1978) showed that LQG has no guaranteed gain or phase margins (can have arbitrarily small gain margin)", | |
| "Observer loop recovery (LTR) is required to restore LQR robustness", | |
| ], | |
| "rubric": {"separation_definition": 2.0, "doyle_counterexample_robustness": 2.0, "ltr_context": 1.0}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_006", | |
| "pillar": "theory_and_concepts", | |
| "topic": "discrete_vs_continuous_stability", | |
| "prompt": "How does the stability boundary differ between continuous-time LTI systems and discrete-time LTI systems? What conformal mapping connects the continuous s-plane to the discrete z-plane, and what happens to imaginary axis poles under exact discretization?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Continuous stability: Re(s) < 0 (left-half plane)", | |
| "Discrete stability: |z| < 1 (strictly inside the unit circle)", | |
| "Conformal mapping: z = e^(s * Ts)", | |
| "Imaginary axis s = j*omega maps onto the unit circle |z| = 1 at angle omega * Ts", | |
| ], | |
| "rubric": {"boundary_definitions": 2.0, "exponential_mapping": 1.5, "imaginary_axis_mapping": 1.5}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_007", | |
| "pillar": "theory_and_concepts", | |
| "topic": "internal_model_principle", | |
| "prompt": "What is the Internal Model Principle (Francis and Wonham)? Explain why an integrator is required in the feedback loop to achieve zero steady-state error for a step reference or constant disturbance.", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Internal Model Principle: To asymptotically track or reject a class of exogenous signals without steady-state error, the feedback loop must contain a dynamic model of that signal generator", | |
| "A step reference/disturbance is generated by 1/s; hence the loop must contain a pole at s=0 (integrator)", | |
| "A sinusoidal reference sin(w*t) requires loop to contain 1/(s^2 + w^2)", | |
| "Provides structural robustness against plant parameter variations", | |
| ], | |
| "rubric": {"francis_wonham_definition": 2.0, "integrator_step_case": 1.5, "harmonic_signal_case": 1.5}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_008", | |
| "pillar": "theory_and_concepts", | |
| "topic": "lqr_robustness_margins", | |
| "prompt": "What are the guaranteed classical stability margins for continuous-time Linear Quadratic Regulators (LQR) with diagonal R matrix? State both the gain margin and phase margin guarantees.", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Guaranteed Gain Margin: GM in [1/2, infinity) (or -6 dB to +infinity dB)", | |
| "Guaranteed Phase Margin: PM >= 60 degrees (at least +/- 60 deg)", | |
| "Result stems from Kalman inequality (return difference inequality): ||I + R^(1/2) K (sI-A)^(-1) B R^(-1/2)|| >= 1 for s=jw", | |
| ], | |
| "rubric": {"gain_margin_values": 2.0, "phase_margin_values": 2.0, "kalman_inequality_origin": 1.0}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_009", | |
| "pillar": "theory_and_concepts", | |
| "topic": "small_gain_vs_passivity", | |
| "prompt": "Contrast the Small Gain Theorem with the Passivity Theorem in nonlinear and robust feedback control. When is passivity preferred over small-gain?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Small Gain Theorem: requires norm product ||G1|| * ||G2|| < 1; gain-based, conservative when loop gain is large at low frequencies", | |
| "Passivity Theorem: requires positive realness / passivity (Re <u, y> >= 0); phase-based, independent of gain magnitude", | |
| "Passivity is preferred in physical systems (mechanical, electrical, robotics) with energy storage and collocated actuators/sensors where infinite gain margin is achievable", | |
| ], | |
| "rubric": {"small_gain_definition": 1.5, "passivity_definition": 1.5, "physical_robotics_preference": 2.0}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_theory_010", | |
| "pillar": "theory_and_concepts", | |
| "topic": "bode_gain_phase_relationship", | |
| "prompt": "Explain Bode's Gain-Phase Relationship for stable minimum-phase systems. What does a -20 dB/decade slope in magnitude imply for the phase at that frequency?", | |
| "ground_truth": { | |
| "key_concepts": [ | |
| "Bode's Gain-Phase theorem: phase angle is uniquely determined by the logarithmic derivative of magnitude with respect to log frequency over all frequencies", | |
| "A constant magnitude slope of -20 dB/decade (-1 log slope) corresponds to approximately -90 degrees of phase shift", | |
| "A slope of -40 dB/decade corresponds to approximately -180 degrees (stability crossover hazard)", | |
| "Applies only to minimum-phase systems (no RHP zeros or delays)", | |
| ], | |
| "rubric": {"gain_phase_integral": 1.5, "slope_phase_correspondence": 2.0, "minimum_phase_condition": 1.5}, | |
| }, | |
| }) | |
| # ========================================================================= | |
| # PILLAR 2: DETERMINISTIC NUMERICAL SYNTHESIS & VERIFICATION (12 items) | |
| # ========================================================================= | |
| items.append({ | |
| "id": "cb_num_001", | |
| "pillar": "numerical_synthesis", | |
| "topic": "exact_zoh_discretization", | |
| "prompt": "Discretize the continuous plant with A=[[0, 1], [-4, -5]], B=[[0], [2]] under exact Zero-Order Hold (ZOH) at sample time Ts=0.1 s. Report the discrete matrices Ad and Bd.", | |
| "ground_truth": { | |
| "tool_call": "exact_zoh", | |
| "arguments": {"A": [[0, 1], [-4, -5]], "B": [[0], [2]], "Ts": 0.1}, | |
| "expected_numeric": { | |
| "Ad": [[0.9814, 0.0772], [-0.3087, 0.5955]], | |
| "Bd": [[0.0093], [0.1543]], | |
| }, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_002", | |
| "pillar": "numerical_synthesis", | |
| "topic": "continuous_lqr", | |
| "prompt": "Design a continuous-time LQR state feedback controller for system A=[[0, 1], [-2, -3]], B=[[0], [1]] with state weighting Q=[[10, 0], [0, 1]] and control penalty R=[[1]]. Compute the Riccati matrix P, gain K, and closed-loop poles.", | |
| "ground_truth": { | |
| "tool_call": "continuous_lqr", | |
| "arguments": {"A": [[0, 1], [-2, -3]], "B": [[0], [1]], "Q": [[10, 0], [0, 1]], "R": [[1]]}, | |
| "expected_numeric": { | |
| "P": [[7.7392, 1.7417], [1.7417, 0.6720]], | |
| "K": [[1.7417, 0.6720]], | |
| }, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_003", | |
| "pillar": "numerical_synthesis", | |
| "topic": "discrete_lqr", | |
| "prompt": "Compute the discrete-time LQR state feedback gain for plant Ad=[[1.0, 0.1], [0.0, 0.95]], Bd=[[0.005], [0.1]] with weights Q=[[5, 0], [0, 1]] and R=[[0.5]]. Report the DARE Riccati matrix P and discrete feedback gain K.", | |
| "ground_truth": { | |
| "tool_call": "discrete_lqr", | |
| "arguments": {"A": [[1.0, 0.1], [0.0, 0.95]], "B": [[0.005], [0.1]], "Q": [[5, 0], [0, 1]], "R": [[0.5]]}, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_004", | |
| "pillar": "numerical_synthesis", | |
| "topic": "cbf_safety_filter", | |
| "prompt": "A single integrator x_dot = u has a state limit x >= -1.0. With barrier h(x) = x - x_min, apply the CBF forward-invariance condition with alpha = 1.5. If the current state is x = -0.7 and nominal control is u_nom = -3.0, compute the filtered safe control input u.", | |
| "ground_truth": { | |
| "tool_call": "cbf_safety_filter", | |
| "arguments": {"x": -0.7, "u_nom": -3.0, "alpha": 1.5, "x_min": -1.0}, | |
| "expected_numeric": {"u_safe": -0.45, "h": 0.3}, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_005", | |
| "pillar": "numerical_synthesis", | |
| "topic": "dynamic_inversion", | |
| "prompt": "For the scalar nonlinear affine system x_dot = 0.5*x + 2.0*u, design a dynamic inversion control input u to track setpoint r = 3.0 from state x = 1.0 with tracking convergence gain k = 4.0.", | |
| "ground_truth": { | |
| "tool_call": "dynamic_inversion", | |
| "arguments": {"a": 0.5, "b": 2.0, "x": 1.0, "reference": 3.0, "gain": 4.0}, | |
| "expected_numeric": {"control_input_u": 3.75, "virtual_control_v": 8.0}, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_006", | |
| "pillar": "numerical_synthesis", | |
| "topic": "kharitonov_stability", | |
| "prompt": "A cubic interval polynomial has ascending-power coefficient bounds lower_bounds=[2.0, 3.0, 4.0, 1.0] and upper_bounds=[2.5, 3.5, 4.5, 1.2]. Apply Kharitonov's Theorem to test whether the system is robustly Hurwitz stable.", | |
| "ground_truth": { | |
| "tool_call": "kharitonov_stability_test", | |
| "arguments": {"lower_bounds": [2.0, 3.0, 4.0, 1.0], "upper_bounds": [2.5, 3.5, 4.5, 1.2]}, | |
| "expected_numeric": {"is_robustly_hurwitz": True}, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_007", | |
| "pillar": "numerical_synthesis", | |
| "topic": "control_allocation", | |
| "prompt": "Given a 3-actuator redundant control effectiveness matrix B=[1.0, 2.0, 3.0], compute the minimum 2-norm control allocation vector u that produces virtual torque tau = 28.0.", | |
| "ground_truth": { | |
| "tool_call": "minimum_norm_control_allocation", | |
| "arguments": {"B": [1.0, 2.0, 3.0], "desired_tau": 28.0}, | |
| "expected_numeric": {"u": [2.0, 4.0, 6.0]}, | |
| "tolerance": 1e-3, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_008", | |
| "pillar": "numerical_synthesis", | |
| "topic": "kalman_measurement_update", | |
| "prompt": "Perform a discrete Kalman filter measurement update for state prediction x_minus=[2.0, 0.0] and covariance P_minus=[[1.0, 0.0], [0.0, 1.0]] with measurement matrix H=[[1.0, 0.0]], measurement noise R=[[0.2]], and observed measurement z=[2.6]. Report the Kalman gain K and updated state x_plus.", | |
| "ground_truth": { | |
| "tool_call": "kalman_measurement_update", | |
| "arguments": { | |
| "x_minus": [2.0, 0.0], | |
| "P_minus": [[1.0, 0.0], [0.0, 1.0]], | |
| "H": [[1.0, 0.0]], | |
| "R": [[0.2]], | |
| "z": [2.6], | |
| }, | |
| "expected_numeric": {"kalman_gain_K": [[0.8333], [0.0]], "x_plus_updated": [2.5, 0.0]}, | |
| "tolerance": 1e-2, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_009", | |
| "pillar": "numerical_synthesis", | |
| "topic": "arx_least_squares", | |
| "prompt": "Estimate parameters theta = [a, b]^T for regressor matrix Phi=[[1.0, 2.0], [2.0, 1.0], [3.0, 4.0]] and output Y=[[5.0], [4.0], [11.0]] using ordinary least squares.", | |
| "ground_truth": { | |
| "tool_call": "least_squares_arx", | |
| "arguments": { | |
| "Phi": [[1.0, 2.0], [2.0, 1.0], [3.0, 4.0]], | |
| "Y": [[5.0], [4.0], [11.0]], | |
| }, | |
| "expected_numeric": {"theta_estimated": [[1.0], [2.0]]}, | |
| "tolerance": 1e-2, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_010", | |
| "pillar": "numerical_synthesis", | |
| "topic": "pole_placement", | |
| "prompt": "Compute state feedback gain K for system A=[[0, 1], [-2, -3]], B=[[0], [1]] to place closed-loop poles at [-4.0, -5.0].", | |
| "ground_truth": { | |
| "tool_call": "place_state_feedback", | |
| "arguments": {"A": [[0, 1], [-2, -3]], "B": [[0], [1]], "desired_poles": [-4.0, -5.0]}, | |
| "expected_numeric": {"K": [[18.0, 6.0]]}, | |
| "tolerance": 1e-2, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_011", | |
| "pillar": "numerical_synthesis", | |
| "topic": "pid_tuning_fopdt", | |
| "prompt": "A temperature process has static gain K=2.5, time constant tau=10.0 s, and delay L=2.0 s. Tune a PID controller for 0% overshoot setpoint tracking using the Chien-Hrones-Reswick (CHR) method.", | |
| "ground_truth": { | |
| "tool_call": "pid_tune_fopdt", | |
| "arguments": {"K_plant": 2.5, "T_tau": 10.0, "L_delay": 2.0, "tuning_objective": "setpoint_tracking_0_overshoot"}, | |
| "expected_numeric": {"Kp": 1.2, "Ki": 0.12, "Kd": 1.2}, | |
| "tolerance": 1e-2, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_num_012", | |
| "pillar": "numerical_synthesis", | |
| "topic": "lyapunov_continuous", | |
| "prompt": "Solve the continuous Lyapunov equation A^T P + P A = -Q for stable matrix A=[[-2, 1], [0, -3]] with identity state weight Q=[[1, 0], [0, 1]].", | |
| "ground_truth": { | |
| "tool_call": "solve_lyapunov", | |
| "arguments": {"A": [[-2, 1], [0, -3]], "Q": [[1, 0], [0, 1]], "discrete": False}, | |
| "expected_numeric": {"P": [[0.25, 0.05], [0.05, 0.1833]]}, | |
| "tolerance": 1e-2, | |
| }, | |
| }) | |
| # ========================================================================= | |
| # PILLAR 3: EXECUTABLE CODE & SIMULATION (10 items) | |
| # ========================================================================= | |
| items.append({ | |
| "id": "cb_code_001", | |
| "pillar": "code_and_simulation", | |
| "topic": "cvxpy_mpc_qp", | |
| "prompt": "Write a clean, runnable Python script using CVXPY to solve a finite-horizon MPC quadratic program for double integrator Ad=[[1.0, 0.1], [0.0, 1.0]], Bd=[[0.0], [0.1]], Q=diag([10, 1]), R=0.1, initial state x0=[2.0, 0.0], horizon N=10, with input constraint |u| <= 1.0. Include assertions verifying solver optimality.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["cvxpy", "numpy"], | |
| "must_execute_cleanly": True, | |
| "must_contain_assertions": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_002", | |
| "pillar": "code_and_simulation", | |
| "topic": "step_response_transients", | |
| "prompt": "Write a Python script using scipy.signal to compute the step response of G(s) = 25 / (s^2 + 6s + 25). Calculate and assert that the natural frequency omega_n = 5.0 rad/s, damping ratio zeta = 0.6, and peak overshoot Mp% is approximately 9.5%.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["scipy", "numpy"], | |
| "must_execute_cleanly": True, | |
| "must_contain_assertions": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_003", | |
| "pillar": "code_and_simulation", | |
| "topic": "bode_stability_margins", | |
| "prompt": "Write a Python script using scipy.signal to compute the Gain Margin, Phase Margin, and crossover frequencies for open-loop transfer function L(s) = 10 / (s*(s + 1)*(s + 2)). Assert that Gain Margin is 6.0 dB and system is stable.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["scipy", "numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_004", | |
| "pillar": "code_and_simulation", | |
| "topic": "kalman_simulation_loop", | |
| "prompt": "Write a Python simulation script for a discrete 1D random walk x[k+1] = x[k] + w[k], y[k] = x[k] + v[k] with Q=0.1, R=1.0 over 50 steps. Implement the predict and update steps, and assert that estimation error covariance P converges to a steady-state value.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_005", | |
| "pillar": "code_and_simulation", | |
| "topic": "feedback_linearization_sim", | |
| "prompt": "Write a Python script implementing feedback linearization for an inverted pendulum on a motor: theta_ddot = 9.81*sin(theta) + u. Choose control u to achieve linear target dynamics theta_ddot + 4*theta_dot + 4*theta = 0. Simulate from theta(0)=0.5 rad and assert asymptotic convergence to 0.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["scipy", "numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_006", | |
| "pillar": "code_and_simulation", | |
| "topic": "state_space_realization", | |
| "prompt": "Write a Python script to convert state space A=[[0, 1], [-2, -3]], B=[[0], [1]], C=[[1, 0]], D=[[0]] into transfer function G(s) using scipy.signal.ss2tf. Assert numerator is [1.0] and denominator is [1.0, 3.0, 2.0].", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["scipy", "numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_007", | |
| "pillar": "code_and_simulation", | |
| "topic": "matlab_zoh_discretization", | |
| "prompt": "Write a clean MATLAB script that defines continuous state-space matrices A=[0 1; -4 -5], B=[0; 2], C=[1 0], D=[0], discretizes them at Ts=0.05 s using c2d(sys, Ts, 'zoh'), extracts Ad and Bd with ssdata, and asserts Ad has size [2, 2].", | |
| "ground_truth": { | |
| "language": "matlab", | |
| "must_contain_matlab_syntax": ["c2d", "ss", "ssdata", "assert"], | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_008", | |
| "pillar": "code_and_simulation", | |
| "topic": "routh_hurwitz_code", | |
| "prompt": "Write a Python function to construct the Routh array for polynomial s^3 + 2s^2 + 3s + K. Test with K=4 (stable) and K=8 (unstable) and assert correct stability determination.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_009", | |
| "pillar": "code_and_simulation", | |
| "topic": "discrete_observer_design", | |
| "prompt": "Write a Python script to design a discrete Luenberger observer for plant Ad=[[0.8, 0.1], [0.0, 0.7]], C=[[1.0, 0.0]] placing observer poles at [0.2, 0.3] using scipy.signal.place_poles. Assert error dynamics eig(Ad - L*C) matches target.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["scipy", "numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_code_010", | |
| "pillar": "code_and_simulation", | |
| "topic": "control_allocation_qp", | |
| "prompt": "Write a Python script using CVXPY to solve control allocation for 4 thrusters B=[[1, 1, 1, 1], [1, -1, 1, -1]] with desired torque tau=[10.0, 0.0] and actuator thrust saturation 0 <= u_i <= 4.0. Assert solver finds optimal feasible thrust allocation.", | |
| "ground_truth": { | |
| "language": "python", | |
| "required_packages": ["cvxpy", "numpy"], | |
| "must_execute_cleanly": True, | |
| }, | |
| }) | |
| # ========================================================================= | |
| # PILLAR 4: UNDERSPECIFICATION, TRAPS & SAFETY GATING (10 items) | |
| # ========================================================================= | |
| items.append({ | |
| "id": "cb_trap_001", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "missing_sample_time", | |
| "prompt": "Discretize the continuous plant with A=[[0, 1], [-2, -3]], B=[[0], [1]] using exact zero-order hold.", | |
| "ground_truth": { | |
| "expected_behavior": "refusal_and_clarification", | |
| "missing_parameters": ["sampling_time_Ts"], | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_002", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "missing_lqr_weights", | |
| "prompt": "Design an optimal continuous-time LQR controller for plant A=[[0, 1], [-1, -2]], B=[[0], [1]].", | |
| "ground_truth": { | |
| "expected_behavior": "refusal_and_clarification", | |
| "missing_parameters": ["state_weight_Q", "control_penalty_R"], | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_003", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "missing_pid_model", | |
| "prompt": "Tune the PID gains Kp, Ki, and Kd for my DC motor.", | |
| "ground_truth": { | |
| "expected_behavior": "refusal_and_clarification", | |
| "missing_parameters": ["plant_transfer_function_or_parameters"], | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_004", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "uncontrollable_system_trap", | |
| "prompt": "Compute state feedback gain K to place closed-loop poles at [-1, -2] for system A=[[1, 0], [0, 2]], B=[[1], [0]].", | |
| "ground_truth": { | |
| "expected_behavior": "error_detection_and_refusal", | |
| "reason": "System is uncontrollable: second state x2 has zero input coupling and unstable pole at +2.", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_005", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "unobservable_observer_trap", | |
| "prompt": "Design a Luenberger observer with poles at [-5, -6] for system A=[[0, 1], [0, 2]], C=[[0, 0]].", | |
| "ground_truth": { | |
| "expected_behavior": "error_detection_and_refusal", | |
| "reason": "System is completely unobservable (C matrix is all zeros).", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_006", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "discrete_hurwitz_trap", | |
| "prompt": "Use the Routh-Hurwitz criterion directly on the discrete-time characteristic polynomial p(z) = z^2 - 1.5z + 0.7 to check stability inside the unit circle.", | |
| "ground_truth": { | |
| "expected_behavior": "conceptual_correction", | |
| "reason": "Routh-Hurwitz tests left-half plane stability (Re(s)<0). For discrete polynomials (unit circle |z|<1), one must either apply the Jury stability test or perform a bilinear transformation z = (1+w)/(1-w) before Routh.", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_007", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "zero_control_authority_trap", | |
| "prompt": "Compute the nonlinear dynamic inversion input u to track r=1.0 for system x_dot = -x + 0.0*u.", | |
| "ground_truth": { | |
| "expected_behavior": "error_detection_and_refusal", | |
| "reason": "Control effectiveness b = 0; system has zero control authority and cannot be inverted.", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_008", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "invalid_covariance_matrix_trap", | |
| "prompt": "Run a Kalman measurement update with prior error covariance P_minus=[[-1.0, 0.0], [0.0, -2.0]].", | |
| "ground_truth": { | |
| "expected_behavior": "error_detection_and_refusal", | |
| "reason": "Covariance matrix P must be positive semi-definite (eigenvalues >= 0). Negative eigenvalues represent impossible negative variance.", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_009", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "missing_mpc_specifications", | |
| "prompt": "Solve the MPC quadratic program for my system to regulate state to origin.", | |
| "ground_truth": { | |
| "expected_behavior": "refusal_and_clarification", | |
| "missing_parameters": ["state_matrix_A", "input_matrix_B", "weights_Q_R", "horizon_N"], | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_trap_010", | |
| "pillar": "underspecified_and_traps", | |
| "topic": "impossible_pole_placement_delay", | |
| "prompt": "Place all closed-loop poles at s = -100 for a continuous system with 500 ms pure time delay e^(-0.5s).", | |
| "ground_truth": { | |
| "expected_behavior": "engineering_critique_and_refusal", | |
| "reason": "Time delay introduces infinite dimensional dynamics with negative phase lag; attempting ultra-fast bandwidth (s = -100) on a 500 ms delay violates phase crossover and induces severe instability.", | |
| "must_not_hallucinate": True, | |
| }, | |
| }) | |
| # ========================================================================= | |
| # PILLAR 5: REAL-WORLD ENGINEERING CASE STUDIES (8 items) | |
| # ========================================================================= | |
| items.append({ | |
| "id": "cb_case_001", | |
| "pillar": "real_world_case_studies", | |
| "topic": "inverted_pendulum_cart", | |
| "prompt": "For an inverted pendulum on a cart with cart mass M=1.0 kg, pendulum mass m=0.1 kg, length l=0.5 m, and g=9.81 m/s^2: linearize the equations of motion around the upright equilibrium (theta=0, x=0), state the continuous matrices (A, B), and formulate the LQR CARE problem.", | |
| "ground_truth": { | |
| "linearized_model": "x = [p, p_dot, theta, theta_dot]^T", | |
| "eigenvalues": "contains one unstable RHP pole at sqrt(g*(M+m)/(M*l)) approx +4.64 rad/s", | |
| "lqr_formulation": "Requires Q >= 0, R > 0 with CARE solve", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_002", | |
| "pillar": "real_world_case_studies", | |
| "topic": "dc_motor_servo", | |
| "prompt": "Derive the state-space representation for an armature-controlled DC motor with armature resistance R=2.0 ohm, inductance L=0.01 H, torque constant Kt=0.05 N*m/A, back-EMF constant Ke=0.05 V/(rad/s), rotor inertia J=0.001 kg*m^2, and viscous friction b=0.0001 N*m*s/rad. State vector is x = [theta, omega, i_a]^T and input is armature voltage V.", | |
| "ground_truth": { | |
| "equations": [ | |
| "theta_dot = omega", | |
| "omega_dot = (Kt/J)*i_a - (b/J)*omega", | |
| "i_a_dot = (1/L)*V - (R/L)*i_a - (Ke/L)*omega", | |
| ], | |
| "dimension": "3x3 state matrix A, 3x1 input matrix B", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_003", | |
| "pillar": "real_world_case_studies", | |
| "topic": "quadrotor_attitude_control", | |
| "prompt": "For a quadrotor UAV attitude dynamics in body frame: J * omega_dot + omega x (J * omega) = tau. Under small angles (hover), state the decoupled roll-pitch-yaw equations of motion and design a PD attitude controller tau_phi = Kp*(phi_des - phi) - Kd*p.", | |
| "ground_truth": { | |
| "decoupled_hover": "I_xx * phi_ddot = tau_phi, I_yy * theta_ddot = tau_theta, I_zz * psi_ddot = tau_psi", | |
| "pd_closed_loop": "phi_ddot + (Kd/I_xx)*phi_dot + (Kp/I_xx)*phi = (Kp/I_xx)*phi_des", | |
| "second_order_tuning": "Kp = I_xx * omega_n^2, Kd = 2 * I_xx * zeta * omega_n", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_004", | |
| "pillar": "real_world_case_studies", | |
| "topic": "quarter_car_suspension", | |
| "prompt": "Formulate the 2-DOF state-space model for a quarter-car active suspension with sprung mass m_s, unsprung mass m_u, suspension spring k_s, suspension damper c_s, tire stiffness k_t, and active actuator force u. Define the state vector and write the equations of motion.", | |
| "ground_truth": { | |
| "states": "x = [z_s - z_u, z_s_dot, z_u - z_r, z_u_dot]^T", | |
| "sprung_eom": "m_s * z_s_ddot = -k_s*(z_s - z_u) - c_s*(z_s_dot - z_u_dot) + u", | |
| "unsprung_eom": "m_u * z_u_ddot = k_s*(z_s - z_u) + c_s*(z_s_dot - z_u_dot) - k_t*(z_u - z_r) - u", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_005", | |
| "pillar": "real_world_case_studies", | |
| "topic": "hdd_voice_coil_actuator", | |
| "prompt": "A hard disk drive voice coil motor positioning head is modeled as a double integrator G(s) = K_v / s^2 with high-frequency resonant modes. Explain why a Notch Filter is placed in series with the lead-lag/PID compensator in the loop.", | |
| "ground_truth": { | |
| "resonance_issue": "Mechanical structural resonance causes high peak gain and phase drop, destabilizing the high-bandwidth track-following loop", | |
| "notch_filter_role": "N(s) = (s^2 + 2*zeta_z*w_n*s + w_n^2)/(s^2 + 2*zeta_p*w_n*s + w_n^2) introduces narrow deep attenuation at resonant frequency w_n without reducing low-frequency loop gain", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_006", | |
| "pillar": "real_world_case_studies", | |
| "topic": "flexible_joint_robot", | |
| "prompt": "A single-link robot with flexible joint is modeled by motor inertia J_m, link inertia J_l, joint torsional stiffness k, and gear damping b. State why this 4th-order system exhibits non-collocated sensor/actuator dynamics if position is measured at the link tip while torque is applied at the motor.", | |
| "ground_truth": { | |
| "collocation": "Collocated (measuring motor position): interlaced alternating poles and zeros on imaginary axis, always passive and easily stabilized", | |
| "non_collocated": "Measuring link tip: transfer function has only poles and no minimum-phase zeros in the mid-frequency range, leading to 360 deg phase lag and severe stability limits", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_007", | |
| "pillar": "real_world_case_studies", | |
| "topic": "aircraft_pitch_autopilot", | |
| "prompt": "For an aircraft short-period longitudinal dynamics: alpha_dot = Z_alpha * alpha + q, q_dot = M_alpha * alpha + M_q * q + M_delta_e * delta_e. Explain the role of Pitch Rate (q) feedback (Pitch Damper) in augmenting short-period damping ratio zeta_sp.", | |
| "ground_truth": { | |
| "short_period_characteristic": "s^2 - (M_q + Z_alpha)*s + (M_q*Z_alpha - M_alpha) = 0", | |
| "pitch_damper_law": "delta_e = -K_q * q", | |
| "damping_augmentation": "Increases the effective M_q derivative (M_q_effective = M_q - M_delta_e * K_q), directly shifting the linear s coefficient and boosting zeta_sp", | |
| }, | |
| }) | |
| items.append({ | |
| "id": "cb_case_008", | |
| "pillar": "real_world_case_studies", | |
| "topic": "maglev_stabilization", | |
| "prompt": "A magnetic levitation system m*y_ddot = m*g - k*(i/y)^2 is open-loop unstable with linearization transfer function G(s) = -K_m / (s^2 - a^2). Explain why pure proportional feedback cannot stabilize this system and design a stabilizing Lead Compensator C(s) = K_c * (s + z)/(s + p).", | |
| "ground_truth": { | |
| "root_locus_failure": "Open-loop poles at s = +a and s = -a. Under pure proportional control, closed-loop poles move along imaginary axis from +/- a towards infinity, remaining on stability boundary with 0 damping", | |
| "lead_compensator_action": "Placing zero z < a and pole p > a pulls root locus branches into the left-half plane, providing phase lead at crossover frequency and ensuring asymptotic stability", | |
| }, | |
| }) | |
| BENCHMARK_PATH.parent.mkdir(parents=True, exist_ok=True) | |
| with BENCHMARK_PATH.open("w", encoding="utf-8") as f: | |
| for item in items: | |
| f.write(json.dumps(item, ensure_ascii=False) + "\n") | |
| print(f"ControlBench v1 successfully built: {len(items)} items written to {BENCHMARK_PATH}") | |
| if __name__ == "__main__": | |
| build_controlbench() | |