Spaces:
Running on Zero
Running on Zero
| """Executable Python and statically checked MATLAB/Simulink SFT families with rich explanations.""" | |
| from __future__ import annotations | |
| from collections.abc import Callable | |
| import cvxpy as cp | |
| import numpy as np | |
| from scipy import linalg, signal | |
| from controlai_data.schema import make_record | |
| OPENERS = ( | |
| "Provide one executable script and report only values computed by it.", | |
| "Write a minimal reproducible implementation for this numerical case.", | |
| "Implement and verify the following calculation; include explicit assertions.", | |
| "Use the requested environment and keep the code self-contained.", | |
| ) | |
| PYTHON = ["python_control_0_10_2", "scipy_docs"] | |
| MATLAB = ["mathworks_control_docs"] | |
| MPC = ["cvxpy_docs", "rawlings_mayne_diehl_mpc_2e"] | |
| def n(x: float) -> str: | |
| if abs(x) < 5e-11: | |
| x = 0.0 | |
| return f"{x:.12g}" | |
| def pylist(x: np.ndarray) -> str: | |
| return repr(np.asarray(x, dtype=float).tolist()) | |
| def matlab(x: np.ndarray) -> str: | |
| a = np.asarray(x, dtype=float) | |
| if a.ndim == 1: | |
| return "[" + " ".join(n(float(v)) for v in a) + "]" | |
| return "[" + "; ".join(" ".join(n(float(v)) for v in row) for row in a) + "]" | |
| def roots(x: np.ndarray) -> list[list[float]]: | |
| x = np.asarray(x, dtype=complex) | |
| x = x[np.lexsort((x.imag, x.real))] | |
| return [[float(v.real), float(v.imag)] for v in x] | |
| def python_tf(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| p1 = -(0.8 + 0.04 * index) | |
| p2 = -(2.0 + 0.03 * index) | |
| z = -(0.4 + 0.02 * index) | |
| gain = 0.7 + 0.025 * index | |
| num = np.array([gain, -gain * z]) | |
| den = np.array([1, -(p1 + p2), p1 * p2]) | |
| dc = num[-1] / den[-1] | |
| code = f"""import control as ct | |
| import numpy as np | |
| # Define numerator and denominator polynomial coefficients | |
| num = {num.tolist()} | |
| den = {den.tolist()} | |
| # Construct continuous-time transfer function | |
| G = ct.tf(num, den) | |
| # Extract transfer function properties | |
| poles = np.sort_complex(ct.poles(G)) | |
| zeros = np.sort_complex(ct.zeros(G)) | |
| dc_gain = float(ct.dcgain(G)) | |
| print("poles:", poles) | |
| print("zeros:", zeros) | |
| print("dc gain:", dc_gain) | |
| # Verification assertions | |
| assert np.allclose(poles, np.sort_complex({[p1, p2]})) | |
| assert np.allclose(zeros, [{z}]) | |
| assert np.isclose(dc_gain, {dc})""" | |
| prompt = f"{OPENERS[index%4]} In Python-control, construct G(s)=({n(num[0])}s+{n(num[1])})/(s^2+{n(den[1])}s+{n(den[2])}), then compute poles, zeros, and DC gain." | |
| answer = f"```python\n{code}\n```\n\n### Explanation and Results\n1. **Transfer Function**: $G(s) = \\frac{{{n(num[0])}s + {n(num[1])}}}{{s^2 + {n(den[1])}s + {n(den[2])}}}$ is constructed using `control.tf`.\n2. **Poles**: The roots of the denominator are $\\lambda = {sorted([p1, p2])}$.\n3. **Zero**: The numerator root is $z = {n(z)}$.\n4. **DC Gain**: Evaluated at $s=0$ as $G(0) = \\frac{{{n(num[1])}}}{{{n(den[2])}}} = {n(dc)}$.\nThe asserted results are verified." | |
| return make_record( | |
| record_id=f"python_tf_{index:05d}", | |
| domain="software_tools", | |
| family="python_control_transfer_properties", | |
| task_type="code", | |
| difficulty="foundation", | |
| template_id=f"python_tf_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "code_tf_properties", | |
| "numerator": num.tolist(), | |
| "denominator": den.tolist(), | |
| "poles": roots(np.array([p1, p2])), | |
| "zeros": roots(np.array([z])), | |
| "dc_gain": dc, | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_tf", | |
| tool="python_control", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_observability(rng: np.random.Generator, index: int) -> dict: | |
| A = rng.integers(-3, 4, size=(3, 3)).astype(float) | |
| C = rng.integers(-2, 3, size=(1, 3)).astype(float) | |
| if not np.any(C): | |
| C[0, 0] = 1 | |
| O = np.vstack([C, C @ A, C @ A @ A]) | |
| rank = int(np.linalg.matrix_rank(O)) | |
| code = f"""import control as ct | |
| import numpy as np | |
| A = np.array({pylist(A)}, dtype=float) | |
| C = np.array({pylist(C)}, dtype=float) | |
| # Construct 3-state observability matrix O = [C; CA; CA^2] | |
| O = ct.obsv(A, C) | |
| r = int(np.linalg.matrix_rank(O)) | |
| print("Observability Matrix:\\n", O) | |
| print("Rank:", r) | |
| assert np.allclose(O, np.array({pylist(O)})) | |
| assert r == {rank}""" | |
| prompt = f"{OPENERS[index%4]} In Python-control, form the observability matrix for A={pylist(A)}, C={pylist(C)} and print its rank and conclusion." | |
| answer = f"```python\n{code}\n```\n\n### Observability Analysis\n- The observability matrix $\\mathcal{{O}} = [C^T, (CA)^T, (CA^2)^T]^T$ is computed via `ct.obsv(A, C)`.\n- Evaluated rank is $\\text{{rank}}(\\mathcal{{O}}) = {rank}$.\n- Conclusion: The 3-state system realization is **{'observable' if rank==3 else 'not observable'}**." | |
| return make_record( | |
| record_id=f"python_observability_{index:05d}", | |
| domain="software_tools", | |
| family="python_control_observability_matrix", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"python_observability_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "observability_rank", | |
| "A": A.tolist(), | |
| "C": C.tolist(), | |
| "O": O.tolist(), | |
| "rank": rank, | |
| "observable": rank == 3, | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_observability", | |
| tool="python_control", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_place(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| a = 0.4 + 0.025 * index | |
| A = np.array([[0, 1], [-a, -0.2]], float) | |
| B = np.array([[0], [1]], float) | |
| desired = np.array([-1.0 - 0.03 * index, -2.0 - 0.04 * index]) | |
| placed = signal.place_poles(A, B, desired) | |
| K = placed.gain_matrix | |
| poles = np.linalg.eigvals(A - B @ K) | |
| code = f"""import numpy as np | |
| from scipy.signal import place_poles | |
| A = np.array({pylist(A)}, dtype=float) | |
| B = np.array({pylist(B)}, dtype=float) | |
| desired = np.array({desired.tolist()}) | |
| # Synthesize state feedback gain K such that eig(A - B*K) = desired | |
| result = place_poles(A, B, desired) | |
| K = result.gain_matrix | |
| closed_poles = np.linalg.eigvals(A - B @ K) | |
| print("State-feedback gain K:", K) | |
| print("Closed-loop poles:", closed_poles) | |
| assert np.allclose(np.sort_complex(closed_poles), np.sort_complex(desired))""" | |
| prompt = f"{OPENERS[index%4]} Use scipy.signal.place_poles for A={pylist(A)}, B={pylist(B)} and desired poles {desired.tolist()}. Print K and verify eig(A-BK)." | |
| answer = f"```python\n{code}\n```\n\n### Pole Placement Synthesis\n- The state feedback gain $K = {pylist(K)}$ is calculated via Ackermann / Kautsky-Nichols algorithm in `place_poles`.\n- Closed-loop system matrix $A_{{cl}} = A - BK$ has eigenvalues exactly at desired locations $\\{{{', '.join(map(str, desired))}\\}}$." | |
| return make_record( | |
| record_id=f"python_place_{index:05d}", | |
| domain="software_tools", | |
| family="python_scipy_state_feedback_pole_placement", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"python_place_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "pole_placement", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "desired_poles": roots(desired), | |
| "K": K.tolist(), | |
| "closed_poles": roots(poles), | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_pole_placement", | |
| tool="scipy", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_lqr(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| a0 = 0.6 + 0.03 * index | |
| a1 = 0.25 + 0.015 * index | |
| A = np.array([[0, 1], [-a0, -a1]], float) | |
| B = np.array([[0], [1]], float) | |
| Q = np.diag([1 + 0.02 * index, 0.5]) | |
| R = np.array([[0.2 + 0.01 * index]]) | |
| P = linalg.solve_continuous_are(A, B, Q, R) | |
| K = np.linalg.solve(R, B.T @ P) | |
| p = np.linalg.eigvals(A - B @ K) | |
| code = f"""import control as ct | |
| import numpy as np | |
| A = np.array({pylist(A)}, dtype=float) | |
| B = np.array({pylist(B)}, dtype=float) | |
| Q = np.array({pylist(Q)}, dtype=float) | |
| R = np.array({pylist(R)}, dtype=float) | |
| # Solve continuous-time LQR optimal control problem | |
| K, P, poles = ct.lqr(A, B, Q, R) | |
| print("LQR gain K:", K) | |
| print("Riccati solution P:", P) | |
| print("Closed-loop poles:", poles) | |
| assert np.allclose(K, np.array({pylist(K)}), rtol=1e-7, atol=1e-8) | |
| assert np.allclose(P, np.array({pylist(P)}), rtol=1e-7, atol=1e-8)""" | |
| prompt = f"{OPENERS[index%4]} Use python-control lqr for A={pylist(A)}, B={pylist(B)}, Q={pylist(Q)}, R={pylist(R)}. Print and verify K, P, and poles." | |
| answer = f"```python\n{code}\n```\n\n### LQR Synthesis\n- CARE solution matrix: $P = {pylist(P)}$.\n- Optimal state feedback gain: $K = R^{{-1}} B^T P = {pylist(K)}$.\n- Closed-loop poles: $\\lambda(A - BK) = {p.tolist()}$, strictly in the open left half plane." | |
| return make_record( | |
| record_id=f"python_lqr_{index:05d}", | |
| domain="software_tools", | |
| family="python_control_continuous_lqr", | |
| task_type="code", | |
| difficulty="advanced", | |
| template_id=f"python_lqr_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "continuous_lqr", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "Q": Q.tolist(), | |
| "R": R.tolist(), | |
| "P": P.tolist(), | |
| "K": K.tolist(), | |
| "closed_poles": roots(p), | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_lqr", | |
| tool="python_control", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_kf(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| x = np.array([0.1 + 0.03 * index, -0.2 + 0.01 * index]) | |
| P = np.array([[1 + 0.02 * index, 0.1], [0.1, 0.6 + 0.01 * index]]) | |
| H = np.array([[1, 0.4]]) | |
| R = np.array([[0.25 + 0.005 * index]]) | |
| z = np.array([0.5 + 0.02 * index]) | |
| innovation = z - H @ x | |
| S = H @ P @ H.T + R | |
| K = P @ H.T @ np.linalg.inv(S) | |
| xp = x + K @ innovation | |
| I = np.eye(2) | |
| Pp = (I - K @ H) @ P @ (I - K @ H).T + K @ R @ K.T | |
| code = f"""import numpy as np | |
| # Prior state estimate and error covariance | |
| x = np.array({x.tolist()}, dtype=float) | |
| P = np.array({pylist(P)}, dtype=float) | |
| H = np.array({pylist(H)}, dtype=float) | |
| R = np.array({pylist(R)}, dtype=float) | |
| z = np.array({z.tolist()}, dtype=float) | |
| # Measurement update equations | |
| innovation = z - H @ x | |
| S = H @ P @ H.T + R | |
| K = P @ H.T @ np.linalg.inv(S) | |
| x_plus = x + K @ innovation | |
| # Joseph form covariance update (guarantees symmetry and positive definiteness) | |
| I = np.eye(len(x)) | |
| P_plus = (I - K @ H) @ P @ (I - K @ H).T + K @ R @ K.T | |
| print("innovation:", innovation) | |
| print("Kalman gain K:", K) | |
| print("x_plus:", x_plus) | |
| print("P_plus:", P_plus) | |
| assert np.allclose(x_plus, np.array({xp.tolist()})) | |
| assert np.allclose(P_plus, np.array({pylist(Pp)}))""" | |
| prompt = f"{OPENERS[index%4]} Implement the Joseph-form 2-state Kalman measurement update in NumPy for x-={x.tolist()}, P-={pylist(P)}, H={pylist(H)}, R={pylist(R)}, z={z.tolist()}." | |
| answer = f"```python\n{code}\n```\n\n### Kalman Measurement Update\n- Innovation: $\\tilde{{y}} = z - H \\hat{{x}}^- = {innovation.tolist()}$.\n- Innovation covariance: $S = H P^- H^T + R = {S.tolist()}$.\n- Kalman Gain: $K = P^- H^T S^{{-1}} = {pylist(K)}$.\n- Posterior state: $\\hat{{x}}^+ = {xp.tolist()}$.\n- Posterior covariance (Joseph form): $P^+ = {pylist(Pp)}$." | |
| return make_record( | |
| record_id=f"python_kf_{index:05d}", | |
| domain="software_tools", | |
| family="python_numpy_joseph_kalman_update", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"python_kf_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "matrix_kf_update", | |
| "x_minus": x.tolist(), | |
| "P_minus": P.tolist(), | |
| "H": H.tolist(), | |
| "R": R.tolist(), | |
| "z": z.tolist(), | |
| "innovation": innovation.tolist(), | |
| "S": S.tolist(), | |
| "K": K.tolist(), | |
| "x_plus": xp.tolist(), | |
| "P_plus": Pp.tolist(), | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_kalman", | |
| tool="numpy", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_zoh(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| T = 0.03 + 0.004 * index | |
| A = np.array([[0, 1], [-1 - 0.02 * index, -0.4]], float) | |
| B = np.array([[0], [1]], float) | |
| C = np.eye(2) | |
| D = np.zeros((2, 1)) | |
| Ad, Bd, _, _, _ = signal.cont2discrete((A, B, C, D), T, method="zoh") | |
| code = f"""import numpy as np | |
| from scipy.signal import cont2discrete | |
| A = np.array({pylist(A)}, dtype=float) | |
| B = np.array({pylist(B)}, dtype=float) | |
| C = np.eye(2) | |
| D = np.zeros((2, 1)) | |
| Ts = {T} | |
| # Zero-order hold exact discretization | |
| Ad, Bd, Cd, Dd, _ = cont2discrete((A, B, C, D), Ts, method="zoh") | |
| print("Discretized Ad:\\n", Ad) | |
| print("Discretized Bd:\\n", Bd) | |
| assert np.allclose(Ad, np.array({pylist(Ad)})) | |
| assert np.allclose(Bd, np.array({pylist(Bd)}))""" | |
| prompt = f"{OPENERS[index%4]} Use scipy.signal.cont2discrete to ZOH-discretize A={pylist(A)}, B={pylist(B)} at Ts={n(T)} s. Print and verify Ad and Bd." | |
| answer = f"```python\n{code}\n```\n\n### ZOH Discretization\n- Sampling period: $T_s = {n(T)}$ s.\n- Discrete state matrix: $A_d = \\exp(A T_s) = {pylist(Ad)}$.\n- Discrete input matrix: $B_d = \\int_0^{{T_s}} \\exp(A \\tau) B d\\tau = {pylist(Bd)}$." | |
| return make_record( | |
| record_id=f"python_zoh_{index:05d}", | |
| domain="software_tools", | |
| family="python_scipy_exact_zoh", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"python_zoh_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "zoh_discretization", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "sample_time": T, | |
| "Ad": Ad.tolist(), | |
| "Bd": Bd.tolist(), | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_zoh", | |
| tool="scipy", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_mpc(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| x0 = -2 + 0.09 * index | |
| r = 0.1 + 0.004 * index | |
| limit = 0.4 + 0.01 * (index % 8) | |
| u = cp.Variable(2) | |
| x1 = x0 + u[0] | |
| x2 = x1 + u[1] | |
| prob = cp.Problem(cp.Minimize(cp.square(x1) + cp.square(x2) + r * cp.sum_squares(u)), [cp.abs(u) <= limit]) | |
| value = prob.solve(solver="CLARABEL") | |
| U = np.asarray(u.value) | |
| X = np.array([x0 + U[0], x0 + U.sum()]) | |
| code = f"""import cvxpy as cp | |
| import numpy as np | |
| x0 = {x0} | |
| r = {r} | |
| limit = {limit} | |
| # Decision variables for control inputs over horizon 2 | |
| u = cp.Variable(2) | |
| x1 = x0 + u[0] | |
| x2 = x1 + u[1] | |
| # Objective: tracking regulation cost + control energy | |
| cost_expr = cp.square(x1) + cp.square(x2) + r * cp.sum_squares(u) | |
| constraints = [cp.abs(u) <= limit] | |
| problem = cp.Problem(cp.Minimize(cost_expr), constraints) | |
| cost = problem.solve(solver="CLARABEL") | |
| assert problem.status in ("optimal", "optimal_inaccurate") | |
| print("Optimal control u*:", u.value) | |
| print("Optimal cost J*:", cost) | |
| assert np.allclose(u.value, np.array({U.tolist()}), atol=1e-6)""" | |
| prompt = f"{OPENERS[index%4]} Implement in CVXPY the horizon-2 integrator MPC with x0={n(x0)}, cost x1^2+x2^2+{n(r)}||u||^2, and |u_i|<={n(limit)}. Solve with CLARABEL and assert the optimum." | |
| answer = f"```python\n{code}\n```\n\n### Convex QP Optimization\n- Optimal input trajectory: $U^* = {U.tolist()}$.\n- Predicted state trajectory: $X^* = {X.tolist()}$.\n- Minimal cost value: $J^* = {n(float(value))}$." | |
| return make_record( | |
| record_id=f"python_mpc_{index:05d}", | |
| domain="software_tools", | |
| family="python_cvxpy_box_mpc", | |
| task_type="code", | |
| difficulty="advanced", | |
| template_id=f"python_mpc_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "mpc_box_qp", | |
| "x0": x0, | |
| "r": r, | |
| "limit": limit, | |
| "U": U.tolist(), | |
| "X": X.tolist(), | |
| "active": [abs(abs(float(v)) - limit) < 1e-6 for v in U], | |
| "cost": float(value), | |
| }, | |
| source_refs=MPC, | |
| verifier="execute_python_and_resolve_qp", | |
| tool="cvxpy_clarabel", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def python_frequency(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| gain = 0.6 + 0.04 * index | |
| tau = 0.2 + 0.025 * index | |
| omega = 0.3 + 0.06 * index | |
| resp = gain / (1 + 1j * tau * omega) | |
| mag = abs(resp) | |
| phase = np.angle(resp) | |
| code = f"""import control as ct | |
| import numpy as np | |
| G = ct.tf([{gain}], [{tau}, 1.0]) | |
| omega = np.array([{omega}]) | |
| mag, phase, omega_out = ct.frequency_response(G, omega) | |
| mag = float(np.squeeze(mag)) | |
| phase = float(np.squeeze(phase)) | |
| print("magnitude:", mag) | |
| print("phase rad:", phase) | |
| assert np.isclose(mag, {mag}) | |
| assert np.isclose(phase, {phase})""" | |
| prompt = f"{OPENERS[index%4]} Use python-control frequency_response for G(s)={n(gain)}/({n(tau)}s+1) at omega={n(omega)} rad/s. Print and assert magnitude and phase." | |
| answer = f"```python\n{code}\n```\n\n### Frequency Response Calculation\n- Linear magnitude at $\\omega = {n(omega)}$ rad/s: $|G(j\\omega)| = {n(mag)}$.\n- Phase response: $\\angle G(j\\omega) = {n(phase)}$ rad ({n(float(np.degrees(phase)))} deg)." | |
| return make_record( | |
| record_id=f"python_frequency_{index:05d}", | |
| domain="software_tools", | |
| family="python_control_frequency_response", | |
| task_type="code", | |
| difficulty="foundation", | |
| template_id=f"python_frequency_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "first_order_frequency", | |
| "gain": gain, | |
| "tau": tau, | |
| "omega": omega, | |
| "magnitude": mag, | |
| "magnitude_db": 20 * np.log10(mag), | |
| "phase_deg": np.degrees(phase), | |
| }, | |
| source_refs=PYTHON, | |
| verifier="execute_python_and_verify_frequency_response", | |
| tool="python_control", | |
| code_language="python", | |
| code_execution="passed", | |
| ) | |
| def matlab_tf(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| p1 = -(1 + 0.04 * index) | |
| p2 = -(3 + 0.03 * index) | |
| z = -(0.5 + 0.02 * index) | |
| gain = 0.8 + 0.02 * index | |
| num = np.array([gain, -gain * z]) | |
| den = np.array([1, -(p1 + p2), p1 * p2]) | |
| dc = num[-1] / den[-1] | |
| code = f"""num = {matlab(num)}; | |
| den = {matlab(den)}; | |
| G = tf(num, den); | |
| p = pole(G); | |
| z = zero(G); | |
| k0 = dcgain(G); | |
| disp('Poles:'); disp(p); | |
| disp('Zeros:'); disp(z); | |
| disp('DC Gain:'); disp(k0); | |
| assert(max(abs(sort(p)-sort({matlab(np.array([p1,p2]))}.'))) < 1e-9); | |
| assert(abs(k0-{n(dc)}) < 1e-9);""" | |
| prompt = f"{OPENERS[index%4]} In MATLAB Control System Toolbox, construct G(s)=({n(num[0])}s+{n(num[1])})/(s^2+{n(den[1])}s+{n(den[2])}), print poles, zero, and DC gain, and assert the results." | |
| answer = f"```matlab\n{code}\n```\n\n### MATLAB Implementation Details\n- `tf(num, den)` creates the transfer function object.\n- `pole(G)` and `zero(G)` compute the roots of the denominator and numerator.\n- `dcgain(G)` evaluates the system transfer function at $s=0$.\n- The computed poles are {sorted([p1, p2])}, zero is {n(z)}, and DC gain is {n(dc)}." | |
| return make_record( | |
| record_id=f"matlab_tf_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_transfer_properties", | |
| task_type="code", | |
| difficulty="foundation", | |
| template_id=f"matlab_tf_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "code_tf_properties", | |
| "numerator": num.tolist(), | |
| "denominator": den.tolist(), | |
| "poles": roots(np.array([p1, p2])), | |
| "zeros": roots(np.array([z])), | |
| "dc_gain": dc, | |
| "required_calls": ["tf(", "pole(", "zero(", "dcgain(", "assert("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_and_verify_tf", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def matlab_observability(rng: np.random.Generator, index: int) -> dict: | |
| A = rng.integers(-3, 4, size=(3, 3)).astype(float) | |
| C = rng.integers(-2, 3, size=(1, 3)).astype(float) | |
| if not np.any(C): | |
| C[0, 0] = 1 | |
| O = np.vstack([C, C @ A, C @ A @ A]) | |
| rank = int(np.linalg.matrix_rank(O)) | |
| code = f"""A = {matlab(A)}; | |
| C = {matlab(C)}; | |
| O = obsv(A, C); | |
| r = rank(O); | |
| disp('Observability Matrix:'); disp(O); | |
| fprintf('rank = %d\\n', r); | |
| assert(norm(O-{matlab(O)}, 'fro') < 1e-9); | |
| assert(r == {rank});""" | |
| prompt = f"{OPENERS[index%4]} In MATLAB, use obsv for A={matlab(A)}, C={matlab(C)}, print the observability matrix/rank, and assert the expected matrix." | |
| answer = f"```matlab\n{code}\n```\n\n### Observability Analysis in MATLAB\n- `obsv(A, C)` generates the observability matrix $\\mathcal{{O}} = [C; CA; CA^2]$.\n- `rank(O)` evaluates matrix rank: $\\text{{rank}}(\\mathcal{{O}}) = {rank}$.\n- Conclusion: The system is **{'observable' if rank==3 else 'not observable'}**." | |
| return make_record( | |
| record_id=f"matlab_observability_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_observability_matrix", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"matlab_observability_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "observability_rank", | |
| "A": A.tolist(), | |
| "C": C.tolist(), | |
| "O": O.tolist(), | |
| "rank": rank, | |
| "observable": rank == 3, | |
| "required_calls": ["obsv(", "rank(", "assert("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_and_verify_observability", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def matlab_place(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| A = np.array([[0, 1], [-0.5 - 0.03 * index, -0.2]], float) | |
| B = np.array([[0], [1]], float) | |
| desired = np.array([-1 - 0.02 * index, -2 - 0.035 * index]) | |
| K = signal.place_poles(A, B, desired).gain_matrix | |
| p = np.linalg.eigvals(A - B @ K) | |
| code = f"""A = {matlab(A)}; | |
| B = {matlab(B)}; | |
| p_des = {matlab(desired)}; | |
| K = place(A, B, p_des); | |
| p_cl = eig(A - B*K); | |
| disp('Gain K:'); disp(K); | |
| disp('Closed-loop poles:'); disp(p_cl); | |
| assert(max(abs(sort(p_cl)-sort(p_des.'))) < 1e-8);""" | |
| prompt = f"{OPENERS[index%4]} In MATLAB, use place for A={matlab(A)}, B={matlab(B)}, desired poles={matlab(desired)}. Print K and assert eig(A-BK)." | |
| answer = f"```matlab\n{code}\n```\n\n### Pole Placement in MATLAB\n- `place(A, B, p_des)` calculates state feedback gain $K = {matlab(K)}$.\n- Closed-loop eigenvalues $\\text{{eig}}(A - BK)$ match target poles $p_{{des}} = {matlab(desired)}$ exactly." | |
| return make_record( | |
| record_id=f"matlab_place_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_state_feedback_pole_placement", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"matlab_place_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "pole_placement", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "desired_poles": roots(desired), | |
| "K": K.tolist(), | |
| "closed_poles": roots(p), | |
| "required_calls": ["place(", "eig(", "assert("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_and_verify_pole_placement", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def matlab_lqr(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| A = np.array([[0, 1], [-0.7 - 0.02 * index, -0.3]], float) | |
| B = np.array([[0], [1]], float) | |
| Q = np.diag([1 + 0.025 * index, 0.5]) | |
| R = np.array([[0.2 + 0.008 * index]]) | |
| P = linalg.solve_continuous_are(A, B, Q, R) | |
| K = np.linalg.solve(R, B.T @ P) | |
| p = np.linalg.eigvals(A - B @ K) | |
| code = f"""A = {matlab(A)}; | |
| B = {matlab(B)}; | |
| Q = {matlab(Q)}; | |
| R = {matlab(R)}; | |
| [K, P, p] = lqr(A, B, Q, R); | |
| disp('LQR Gain K:'); disp(K); | |
| disp('Riccati Matrix P:'); disp(P); | |
| disp('Closed-loop poles:'); disp(p); | |
| assert(norm(K-{matlab(K)}, 'fro') < 1e-7); | |
| assert(max(real(p)) < 0);""" | |
| prompt = f"{OPENERS[index%4]} In MATLAB, solve continuous LQR for A={matlab(A)}, B={matlab(B)}, Q={matlab(Q)}, R={matlab(R)}; print K,P,p and assert stability." | |
| answer = f"```matlab\n{code}\n```\n\n### Continuous LQR in MATLAB\n- `[K, P, p] = lqr(A, B, Q, R)` solves CARE $A^T P + PA - P B R^{{-1}} B^T P + Q = 0$.\n- Optimal feedback gain: $K = {matlab(K)}$.\n- Closed-loop poles: $p = {p.tolist()}$, confirming closed-loop stability." | |
| return make_record( | |
| record_id=f"matlab_lqr_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_continuous_lqr", | |
| task_type="code", | |
| difficulty="advanced", | |
| template_id=f"matlab_lqr_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "continuous_lqr", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "Q": Q.tolist(), | |
| "R": R.tolist(), | |
| "P": P.tolist(), | |
| "K": K.tolist(), | |
| "closed_poles": roots(p), | |
| "required_calls": ["lqr(", "assert("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_and_verify_lqr", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def matlab_zoh(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| T = 0.025 + 0.004 * index | |
| A = np.array([[0, 1], [-1 - 0.025 * index, -0.35]], float) | |
| B = np.array([[0], [1]], float) | |
| C = np.eye(2) | |
| D = np.zeros((2, 1)) | |
| Ad, Bd, _, _, _ = signal.cont2discrete((A, B, C, D), T) | |
| code = f"""A = {matlab(A)}; | |
| B = {matlab(B)}; | |
| C = eye(2); | |
| D = zeros(2,1); | |
| Ts = {n(T)}; | |
| sys_c = ss(A, B, C, D); | |
| sysd = c2d(sys_c, Ts, 'zoh'); | |
| [Ad, Bd, ~, ~] = ssdata(sysd); | |
| disp('Ad:'); disp(Ad); | |
| disp('Bd:'); disp(Bd); | |
| assert(norm(Ad-{matlab(Ad)}, 'fro') < 1e-8); | |
| assert(norm(Bd-{matlab(Bd)}, 'fro') < 1e-8);""" | |
| prompt = f"{OPENERS[index%4]} In MATLAB, use ss, c2d, and ssdata to ZOH-discretize A={matlab(A)}, B={matlab(B)} at Ts={n(T)} s; assert Ad and Bd." | |
| answer = f"```matlab\n{code}\n```\n\n### ZOH Discretization in MATLAB\n- `ss(A, B, C, D)` constructs the continuous-time state-space object.\n- `c2d(sys_c, Ts, 'zoh')` performs exact Zero-Order Hold discretization at sample time $T_s = {n(T)}$ s.\n- `ssdata(sysd)` extracts the discrete matrices $A_d = {matlab(Ad)}$ and $B_d = {matlab(Bd)}$." | |
| return make_record( | |
| record_id=f"matlab_zoh_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_exact_zoh_state_space", | |
| task_type="code", | |
| difficulty="intermediate", | |
| template_id=f"matlab_zoh_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "zoh_discretization", | |
| "A": A.tolist(), | |
| "B": B.tolist(), | |
| "sample_time": T, | |
| "Ad": Ad.tolist(), | |
| "Bd": Bd.tolist(), | |
| "required_calls": ["ss(", "c2d(", "ssdata(", "assert("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_and_verify_zoh", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def matlab_frequency_plots(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| gain = 0.8 + 0.03 * index | |
| a1 = 1.0 + 0.04 * index | |
| a0 = 2.0 + 0.05 * index | |
| num = [gain] | |
| den = [1, a1, a0] | |
| code = f"""G = tf({matlab(np.array(num))}, {matlab(np.array(den))}); | |
| p = pole(G); | |
| stable = isstable(G); | |
| fprintf('stable = %d\\n', stable); | |
| disp('Poles:'); disp(p); | |
| figure; step(G); grid on; title('Step response'); | |
| figure; bode(G); grid on; | |
| [gm, pm, wcg, wcp] = margin(G); | |
| fprintf('GM ratio = %.6g, PM deg = %.6g, Wcg = %.6g, Wcp = %.6g\\n', gm, pm, wcg, wcp);""" | |
| poles = np.roots(den) | |
| prompt = f"{OPENERS[index%4]} In MATLAB, construct G(s)={n(gain)}/(s^2+{n(a1)}s+{n(a0)}), print poles/stability, plot step and Bode responses, and compute margins without inventing outputs." | |
| answer = f"```matlab\n{code}\n```\n\n### MATLAB Frequency Workflow\n- `tf`, `pole`, `isstable`, `step`, `bode`, and `margin` are used to analyze $G(s) = \\frac{{{n(gain)}}}{{s^2 + {n(a1)}s + {n(a0)}}}$.\n- Analytical poles: {poles.tolist()}." | |
| return make_record( | |
| record_id=f"matlab_frequency_plots_{index:05d}", | |
| domain="software_tools", | |
| family="matlab_step_bode_margin_workflow", | |
| task_type="code", | |
| difficulty="foundation", | |
| template_id=f"matlab_frequency_plots_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "polynomial_stability", | |
| "coefficients": den, | |
| "poles": roots(poles), | |
| "stable": bool(np.all(np.real(poles) < 0)), | |
| "required_calls": ["tf(", "pole(", "isstable(", "step(", "bode(", "margin("], | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_matlab_frequency_workflow", | |
| tool="matlab_control_system_toolbox", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| def simulink_pid(rng: np.random.Generator, index: int) -> dict: | |
| del rng | |
| kp = 0.8 + 0.03 * index | |
| ki = 0.2 + 0.01 * index | |
| kd = 0.05 + 0.004 * index | |
| a = 1.0 + 0.04 * index | |
| model = f"controlai_pid_{index:03d}" | |
| code = f"""mdl = '{model}'; | |
| if bdIsLoaded(mdl), close_system(mdl, 0); end | |
| new_system(mdl); | |
| open_system(mdl); | |
| add_block('simulink/Sources/Step', [mdl '/Step'], 'Position', [30 80 60 110]); | |
| add_block('simulink/Math Operations/Sum', [mdl '/Sum'], 'Inputs', '+-', 'Position', [100 78 125 112]); | |
| add_block('simulink/Continuous/PID Controller', [mdl '/PID'], 'P', '{n(kp)}', 'I', '{n(ki)}', 'D', '{n(kd)}', 'Position', [165 75 245 115]); | |
| add_block('simulink/Continuous/Transfer Fcn', [mdl '/Plant'], 'Numerator', '[1]', 'Denominator', '[1 {n(a)}]', 'Position', [285 75 365 115]); | |
| add_block('simulink/Sinks/Scope', [mdl '/Scope'], 'Position', [415 78 445 112]); | |
| add_line(mdl, 'Step/1', 'Sum/1', 'autorouting', 'on'); | |
| add_line(mdl, 'Sum/1', 'PID/1', 'autorouting', 'on'); | |
| add_line(mdl, 'PID/1', 'Plant/1', 'autorouting', 'on'); | |
| add_line(mdl, 'Plant/1', 'Scope/1', 'autorouting', 'on'); | |
| add_line(mdl, 'Plant/1', 'Sum/2', 'autorouting', 'on'); | |
| set_param(mdl, 'StopTime', '10'); | |
| save_system(mdl); | |
| out = sim(mdl);""" | |
| prompt = f"{OPENERS[index%4]} Write MATLAB code that programmatically builds and simulates Step -> Sum -> PID -> Transfer Fcn -> Scope with unity negative feedback. Use placeholder PID [{n(kp)},{n(ki)},{n(kd)}] and plant 1/(s+{n(a)}), clearly labeled as demonstration values." | |
| answer = f"```matlab\n{code}\n```\n\n### Simulink Programmatic Construction\n- Programmatically builds a closed-loop PID control loop in Simulink using `new_system`, `add_block`, `add_line`, and runs simulation via `sim(mdl)`.\n- Gains $K_p={n(kp)}, K_i={n(ki)}, K_d={n(kd)}$ are demonstration parameters." | |
| required = [ | |
| "new_system(", | |
| "open_system(", | |
| "add_block(", | |
| "simulink/Sources/Step", | |
| "simulink/Math Operations/Sum", | |
| "simulink/Continuous/PID Controller", | |
| "simulink/Continuous/Transfer Fcn", | |
| "simulink/Sinks/Scope", | |
| "add_line(", | |
| "set_param(", | |
| "save_system(", | |
| "sim(", | |
| ] | |
| return make_record( | |
| record_id=f"simulink_pid_{index:05d}", | |
| domain="software_tools", | |
| family="simulink_programmatic_pid_feedback", | |
| task_type="code", | |
| difficulty="advanced", | |
| template_id=f"simulink_pid_prompt_{index%4}", | |
| prompt=prompt, | |
| answer=answer, | |
| ground_truth={ | |
| "kind": "simulink_static", | |
| "model": model, | |
| "kp": kp, | |
| "ki": ki, | |
| "kd": kd, | |
| "plant_den": [1, a], | |
| "required_calls": required, | |
| "sum_inputs": "+-", | |
| "forward_connections": 4, | |
| "feedback_connections": 1, | |
| }, | |
| source_refs=MATLAB, | |
| verifier="static_simulink_pid_model", | |
| tool="matlab_simulink", | |
| code_language="matlab", | |
| code_execution="static_checked", | |
| ) | |
| FAMILIES: tuple[tuple[str, Callable[[np.random.Generator, int], dict]], ...] = ( | |
| ("python_control_transfer_properties", python_tf), | |
| ("python_control_observability_matrix", python_observability), | |
| ("python_scipy_state_feedback_pole_placement", python_place), | |
| ("python_control_continuous_lqr", python_lqr), | |
| ("python_numpy_joseph_kalman_update", python_kf), | |
| ("python_scipy_exact_zoh", python_zoh), | |
| ("python_cvxpy_box_mpc", python_mpc), | |
| ("python_control_frequency_response", python_frequency), | |
| ("matlab_transfer_properties", matlab_tf), | |
| ("matlab_observability_matrix", matlab_observability), | |
| ("matlab_state_feedback_pole_placement", matlab_place), | |
| ("matlab_continuous_lqr", matlab_lqr), | |
| ("matlab_exact_zoh_state_space", matlab_zoh), | |
| ("matlab_step_bode_margin_workflow", matlab_frequency_plots), | |
| ("simulink_programmatic_pid_feedback", simulink_pid), | |
| ) | |
| def generate_code_v1(count_per_family: int, seed: int) -> list[dict]: | |
| rng = np.random.default_rng(seed) | |
| return [ | |
| generator(rng, index) | |
| for _, generator in FAMILIES | |
| for index in range(1, count_per_family + 1) | |
| ] | |