from __future__ import annotations from typing import Optional import numpy as np MODE_TO_ROW = {"11": 0, "22": 3, "12": 6} LATERAL_ROW = {"11": 1, "22": 4, "12": None} EPS33_ROW = {"11": 2, "22": 5, "12": None} def evaluate_cubic_no_intercept(coeffs: np.ndarray, x: np.ndarray) -> np.ndarray: coeffs = np.asarray(coeffs, dtype=np.float32).reshape(-1) x = np.asarray(x, dtype=np.float32) coeffs_with_zero = np.append(coeffs, 0.0) return np.polyval(coeffs_with_zero, x).astype(np.float32) def fit_cubic_no_intercept(x: np.ndarray, y: np.ndarray, degree: int = 3) -> Optional[np.ndarray]: x = np.asarray(x, dtype=np.float32).reshape(-1) y = np.asarray(y, dtype=np.float32).reshape(-1) if x.size < 2 or y.size < 2 or x.size != y.size: return None degree = int(max(1, min(int(degree), max(1, x.size - 1)))) A = np.vstack([x**p for p in range(degree, 0, -1)]).T try: coeffs, *_ = np.linalg.lstsq(A, y, rcond=None) return coeffs.astype(np.float32) except Exception: return None def default_x_for_mode(mode: str) -> np.ndarray: x_max = 0.1 return np.linspace(0.0, x_max, 250, dtype=np.float32)