From 24582be5bf7f4451e92e4263f948be8399762408 Mon Sep 17 00:00:00 2001 From: Nicholas Bergantz <64328463+kopecn@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:28:11 -0700 Subject: [PATCH 1/2] Feat/update template module (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updating help for makefile * updating template module * cleanups * cont... * cool... * cool... * scoping out updates and upgrades. * 01 Template rename and refresh Rename pyMathTools/pyMathToolsPlotHelpers packages to snake_case (math_tools/math_plot_helpers) and re-parent QuaternionABC onto the new foundation_abc layout per the template conformance spec. * 02 Errors and layering Add the math_tools exception hierarchy (MathToolsError and three domain subtypes) and an AST-based package layering test enforcing math_tools never imports math_plot_helpers/matplotlib, and math_tools.otg (once it exists) never imports numpy. * 03 Governance and readme Add .claude/CLAUDE.md and a real README, correct pyproject dependency names (numpy/scipy were missing), and fix dead MathTypes import paths in the two spherical plotting examples so they import-run cleanly. * updating the github actions * fixes for ci-cd * 04 Precision time interval Add PrecisionTimeInterval: an immutable, hashable, attosecond-exact subclass of PrecisionTimeIntervalABC backed by a single signed total- attosecond int, with full arithmetic/ordering/wire-dict surface per precisionTimeMath.md. * 05 Precision timestamp Add PrecisionTimestamp: an immutable, hashable, epoch-referenced timestamp composing a PrecisionTimeInterval offset plus optional timescale/reference_frame/uncertainty metadata, with Swift-parity can_compare/compare_validated semantics per precisionTimeMath.md. * 06 Position Add Position: a numpy-backed 3D point type subclassing PositionABC with cylindrical/spherical/spherical-iso constructors and inverse accessors, full vector arithmetic, and the shared API idioms (normalize/normalized, isclose, __array__, unhashable). * 07 Quaternion additions Add dot, rotation_matrix_elements (delegating to to_rotation_matrix), rotate_position, __array__, and snake_case aliases for the two existing camelCase methods to Quaternion. Purely additive: legacy tests/test_quaternion.py is untouched. * 08 Spatial pose Add SpatialPose: an SE(3) pose composing Position + Quaternion, subclassing SpatialTransformABC. Constructors (components, homogeneous matrix, Denavit-Hartenberg, identity), composition/transform via *, translated, inverse, relative_pose, interpolate (lerp + slerp), position/angular distance (double-cover safe), and normalize/normalized per spatialMath.md. * 09 Univariate polynomial Add UnivariatePolynomial: an immutable, numpy-backed general polynomial with ascending-degree float64 coefficient storage, derivative/integrate, companion-matrix real_roots (raises PolynomialSolveError on the zero polynomial), and value evaluation via numpy's polyval. * 10 Roots kernel Faithful port of the Swift OTG analytic root kernel (Roots.swift/Utils.swift): solve_cubic, solve_resolvent, solve_quartic_monic, evaluate_polynomial, polynomial_derivative(_monic), shrink_interval, integrate_jerk. Corrects a latent 1/a^4 vs 1/a^3 scaling bug in solve_cubic's halfq term (inert in the Swift source's monic-only call sites, not inert for general coefficients per Compliance 2b), documented in polynomials.md. Re-homes POLYNOMIAL_ZERO_THRESHOLD from polynomial.py into roots.py. Co-Authored-By: Claude Sonnet 5 * 11 Waveform1D core container Adds the scalar waveform container: attosecond-exact PrecisionTime axis, 22 signal generators, None-on-empty statistics (population variance pinned), attosecond-exact slicing, and the list-vocabulary mutation API. No DSP mixins yet (composed in a later chunk); no arithmetic operators (chunk 12). Co-Authored-By: Claude Sonnet 5 * 12 Waveform1D operators & comparison Adds the elementwise operator surface: arithmetic (+ - * / // %, forward/ reflected/in-place), bitwise (integer-dtype only), unary, and comparison producers (elements_equal/less_than/greater_than, isclose_elementwise, isclose). Waveform-Waveform ops require equal dt+length (WaveformCompatibilityError otherwise); results carry dt/t0 from the left operand. Fixes a bug where Waveform1D's __array__ let numpy's ufunc machinery bypass Python's operator protocol for numpy-scalar/array operands (e.g. np.float64(2.0) + w silently returned a bare ndarray, discarding dt/t0) by setting __array_ufunc__ = None, with a regression test. Co-Authored-By: Claude Sonnet 5 * 13 Waveform1D signal generators The 22 generators themselves landed early in chunk 11 (umbrella spec groups them under Constructors). This chunk adds the dedicated compliance-4 test file, and fixes a real gap: sine's frequency parameter had no default, so the spec's own pinned example (Waveform1D.sine(n=1000)) raised TypeError. frequency now defaults to 1.0 Hz on sine only, matching the literal acceptance criterion. Co-Authored-By: Claude Sonnet 5 * 14 DSP support types + mixin protocol Adds the shared substrate for the upcoming DSP mixin chunks: all 12 support enums + 15 frozen descriptor dataclasses (eq=False on the 5 ndarray-bearing ones), the WaveformProtocol structural contract mixins will depend on, and Waveform1D._with_values (the replace-values factory). WaveformTriggerType (undefined in the Python spec text) is flattened from Swift's associated-value enum into a 4-member discriminator, with the payload moved onto WaveformTrigger's optional fields. Co-Authored-By: Claude Sonnet 5 * 15 WaveformPosition Adds the position time-series aggregate container: (n,3) float64 storage, on-demand Position materialization, from_components with length/dt validation, vectorized normalize/component_waveforms (never loops over materialized elements), and the full mutation/extend/concatenate verb set. Co-Authored-By: Claude Sonnet 5 * 16 WaveformQuaternion Adds the quaternion time-series aggregate container, mirroring WaveformPosition's structure: (n,4) float64 storage in w-first order (pinned repo-wide, not Swift's x-first), on-demand Quaternion materialization, w-first component_waveforms/from_components, vectorized are_all_unit/normalize, and the full mutation/extend/concatenate verb set. Co-Authored-By: Claude Sonnet 5 * 17 WaveformSpatialPose Adds the 6-DOF pose time-series container: parallel (n,3) position + (n,4) w-first quaternion arrays, from_poses/from_waveforms constructors, nested component_waveforms reusing chunks 15/16's per-axis NamedTuples, and the is_valid/sample_count=min(...) semantics for the (deliberately constructible) unequal-array-length edge case. Completes Track C (chunks 11-17). Co-Authored-By: Claude Sonnet 5 * 18 DspCalcMixin Add CalcMixin (integrate/derivative). Fixed a runtime bug in the planned mixin pattern: nominally subclassing WaveformProtocol shadows Waveform1D's real properties under C3 linearization, so mixins now self-type instead (mypy's documented "mixin classes" idiom). Spec and protocol docs updated accordingly for chunks 19-29. * 19 DspCorrelationMixin Add CorrelationMixin (auto_correlation, cross_correlation, find_max_correlation), backed by scipy.signal.correlate. Lag sign kept as scipy's native convention (spec-permitted divergence from Swift), documented in the module docstring. * 20 DspEnvelopeMixin Add EnvelopeMixin: amplitude_envelope (Hilbert), upper_lower_envelopes (peak/valley interpolation), instantaneous_amplitude (HILBERT/RMS/PEAK). * 21 DspSpectralMixin Add SpectralMixin: fft, power_spectral_density (Welch), spectrogram, mel_spectrogram, spectral_features (centroid/spread/rolloff/flatness). * 22 DspFilteringMixin Add FilteringMixin: low/high/band-pass, dispatcher (incl. band-stop), moving-average, exponential, Savitzky-Golay, Whittaker-Henderson filters, and frequency_response. * 23 DspPeakMixin Add PeakMixin: detect_peaks, detect_valleys, find_most_prominent_peaks, backed by scipy.signal.find_peaks/peak_prominences. * 24 DspPhaseMixin Add PhaseMixin: instantaneous_phase, unwrap_phase, instantaneous_frequency, phase_difference, phase_coherence, phase_synchronization_index, group_delay. * 25 DspResamplingMixin Add ResamplingMixin: decimated, interpolated, resampled, resampled_to_match, polyphase_resampled. Adds WaveformProtocol._with_axis (dt-changing factory sibling to _with_values) since resampling is the first DSP family that changes sample spacing. * 26 DspTimeAlignmentMixin Add TimeAlignmentMixin: aligned, time_lag, synchronize, time_windows, time_segments. Adds WaveformProtocol._with_t0 (t0-changing factory, completing the _with_values/_with_axis/_with_t0 result-factory triangle), since this is the first DSP family whose results carry a different t0 than their source. * 27 DspTriggerMixin Add TriggerMixin: detect_edge_triggers, detect_level_triggers, detect_window_triggers, detect_pattern_triggers, generic detect_triggers dispatcher, with_event_markers. WaveformTrigger gains optional edge/ window_kind fields for the dispatcher; WaveformWithEvents.waveform is now typed WaveformProtocol instead of concrete Waveform1D, which also removes support.py's only import of waveform1d.py (preempts the import cycle flagged in chunk 19). * 28 DspWindowingMixin Add WindowingMixin: generate_window, windowed, window_coherent_gain, window_processing_gain. * 29 DspZeroCrossingMixin Add ZeroCrossingMixin: zero_crossings, zero_crossing_count, zero_crossing_rate, segments_between_zero_crossings. * 30 DspCompose Compose all twelve DSP mixins onto Waveform1D in the spec-pinned base order. Fixes a circular import surfaced by dsp/__init__.py now eagerly re-exporting every mixin (support.py's WaveformProtocol import moved under TYPE_CHECKING). Mixin test files drop their local `class _W(Mixin, Waveform1D)` subclasses in favor of plain Waveform1D. Adds test_compose.py: MRO/concreteness pins plus a live smoke call per mixin family on a real Waveform1D instance. * 31 OtgEnumsAndErrors * 32 OtgInputParameter * 33 OtgProfile * 34 OtgBlockBrakeBound * 35 OtgTrajectoryAndOutput * 36 OtgVelocitySteps * 37 OtgPositionFirstSecondSteps * 38 OtgPositionThirdStep1 * 39 OtgPositionThirdStep2 * 40 OtgCalculatorTarget * 41 OtgDriver Co-Authored-By: Claude Sonnet 5 * 42 OtgOracleSuites Corrects otg.md's numeric-truth-table atol from 1e-8 to 1e-6 (semver 0.0.4 -> 0.0.5): the Swift source literals are recorded to only 6 decimal places, so 1e-8 was unsatisfiable by any correct port. Also corrects "32-case" to "31-case" (the Swift array has 31 entries). Co-Authored-By: Claude Sonnet 5 * 43 PublicSurface Final chunk of the math-tools-port plan: curates the flat `import math_tools as mt` root surface (19 re-exports, no logic), pinned by test_public_surface.py. Co-Authored-By: Claude Sonnet 5 * Mark math-tools-port plan complete (all 43 chunks done) Co-Authored-By: Claude Sonnet 5 * 44 OtgStep2UdudDiscriminant Fix j·tf⁴→j·tf³ transcription defect in UDUD-T0246 discriminant (tf_p3→tf_p2) at position_third_order_step2.py:1731,1752,1763; add regression test. Scaffold Track F corrective plan overview. Co-Authored-By: Claude Opus 4.8 * 45 OtgTrivialProfileLength Trivial branch builds fixed-length 8-element a/v/p arrays and sets pf to current position, per otg.md fidelity requirement 1; add regression tests. Co-Authored-By: Claude Opus 4.8 * 46 OtgStep1SqrtGuard Replace 8 bare math.sqrt sites in position_third_order_step1 with the IEEE-754 _ieee754_sqrt (NaN on negative radicand) so out-of-domain candidates are rejected by guards instead of raising; add regression test. Co-Authored-By: Claude Opus 4.8 * 47 PositionToleranceAndTimestampAccessors Position.is_unit uses rtol=0.0 so only spec atol=1e-12 applies; days_since_epoch/seconds_of_day derive from signed total_attoseconds (floor div/mod) for correct pre-epoch behavior. Add regression tests. Co-Authored-By: Claude Opus 4.8 * 48 RootsDegenerateCases solve_cubic uses a scale-relative leading-coefficient degeneracy threshold; shrink_interval guards f==0 before the Newton step to avoid a ZeroDivisionError trap. Update polynomials.md (semver 0.0.4) and add regression tests. Co-Authored-By: Claude Opus 4.8 * 49 WaveformSpatialPoseIndexing Route __getitem__/get/pop through a shared _resolve_index helper that bounds against sample_count (not either raw array's length), per waveformCore.md compliance 9; add regression tests. Co-Authored-By: Claude Opus 4.8 * 50 AggregateContainerApiIdioms Add __array__ and isclose to the three aggregate waveform containers, restore WaveformSpatialPose.from_components, and fix Waveform1D.__array__ copy=False contract (C-7). Update waveformCore.md (semver 0.0.3) and add tests. Co-Authored-By: Claude Opus 4.8 * 51 WaveformGeneratorPhaseConventions chirp/heaviside/sigmoid derive total duration from an endpoint-inclusive (n-1)*dt span via _span_seconds (0.0 at n<=1), fixing off-by-one phase conventions. Update waveformCore.md (semver 0.0.4) and add tests. Co-Authored-By: Claude Opus 4.8 * 52 SubpackagePublicSurface Give math_tools.spherical and math_plot_helpers curated __all__ re-exports; update README/examples to import at subpackage level; pin the surface with tests. Co-Authored-By: Claude Opus 4.8 * 53 WindowConventionReconciliation Reconcile window symmetric/periodic conventions: add periodic param to windowing helpers (default symmetric), share DEFAULT_KAISER_BETA across windowing/spectral. Document per-family rule in waveformDsp.md (semver 0.0.8) and add tests. Co-Authored-By: Claude Opus 4.8 * 54 TestClosureTracksBC Close 11 Track B/C test-coverage gaps (hashability, double-cover isclose, operator surface, generator analytics, bulk smoke, indexing); set Quaternion.__hash__ = None explicitly (the one permitted src edit). Co-Authored-By: Claude Opus 4.8 * 55 TestClosureDsp Close 23 DSP test-coverage gaps with value-level known-answer/analytic assertions across all DSP mixin families; tests only, no src changes. Co-Authored-By: Claude Opus 4.8 * 56 TestClosureOtgOracles Close 8 OTG oracle-coverage gaps: add a reproducible 450-case multi-DOF corpus that exercises Step2 (0->900 invocations), plus continuity, retarget, invariant, and the missing testBugFix_NegativeTimeInterval_Case3 cases. Correct chunk 42's stale 'all 4 testBugFix' claim. Tests/data only. Co-Authored-By: Claude Opus 4.8 * 57 DocsAndConventionSweep Reconcile docs against post-fix repo: fix dead CI/workflow refs, stale OTG tolerance (1e-6) and case-count claims, install/keyword drift; wire examples/ into the gate (lint+typecheck clean); delete hints.py dead __main__; add governance tests. Bump templateConformance (0.0.3) and overview (0.1.1). Co-Authored-By: Claude Opus 4.8 * Mark math-tools-port plan complete (Track F, all 57 chunks done) Co-Authored-By: Claude Opus 4.8 * clean room installs requirements.txt before the package pyproject declares dependency NAMES ONLY per the BKM; requirements.txt carries the pins and git pointers. testInEnvInstallFromSetup ran `pip install ".[dev]"` alone, so pip resolved the bare name pyFoundationTools against PyPI and failed with "No matching distribution found". Every other install path here already installs -r requirements.txt first. make testInEnv now green: 1550 passed. Co-Authored-By: Claude Opus 5 * rename clean-room sub-steps so they stop implying tests Back-port of the py-cookiecut rename. testInEnv green end-to-end after the rename (1550 passed in the clean room). Co-Authored-By: Claude Opus 5 * cont.... * bump ver --------- Co-authored-by: Claude Sonnet 5 --- .bumpversion.cfg | 10 + .claude/CLAUDE.md | 78 + .../archive/math-tools-port/00-overview.md | 195 + .../01-template-rename-and-refresh.md | 123 + .../math-tools-port/02-errors-and-layering.md | 74 + .../03-governance-and-readme.md | 106 + .../04-precision-time-interval.md | 109 + .../math-tools-port/05-precision-timestamp.md | 84 + .../archive/math-tools-port/06-position.md | 89 + .../07-quaternion-additions.md | 84 + .../math-tools-port/08-spatial-pose.md | 98 + .../09-univariate-polynomial.md | 106 + .../math-tools-port/10-roots-kernel.md | 128 + .../math-tools-port/11-waveform1d-core.md | 130 + .../12-waveform1d-operators.md | 97 + .../13-waveform1d-generators.md | 105 + .../14-dsp-support-and-protocol.md | 128 + .../math-tools-port/15-waveform-position.md | 96 + .../math-tools-port/16-waveform-quaternion.md | 94 + .../17-waveform-spatial-pose.md | 107 + .../archive/math-tools-port/18-dsp-calc.md | 138 + .../math-tools-port/19-dsp-correlation.md | 108 + .../math-tools-port/20-dsp-envelope.md | 106 + .../math-tools-port/21-dsp-spectral.md | 99 + .../math-tools-port/22-dsp-filtering.md | 127 + .../archive/math-tools-port/23-dsp-peaks.md | 91 + .../archive/math-tools-port/24-dsp-phase.md | 94 + .../math-tools-port/25-dsp-resampling.md | 107 + .../math-tools-port/26-dsp-time-alignment.md | 147 + .../math-tools-port/27-dsp-triggers.md | 117 + .../math-tools-port/28-dsp-windowing.md | 93 + .../math-tools-port/29-dsp-zero-crossings.md | 88 + .../archive/math-tools-port/30-dsp-compose.md | 95 + .../31-otg-enums-and-errors.md | 79 + .../math-tools-port/32-otg-input-parameter.md | 91 + .../archive/math-tools-port/33-otg-profile.md | 136 + .../34-otg-block-brake-bound.md | 132 + .../35-otg-trajectory-and-output.md | 119 + .../math-tools-port/36-otg-velocity-steps.md | 118 + .../37-otg-position-first-second-steps.md | 108 + .../38-otg-position-third-step1.md | 118 + .../39-otg-position-third-step2.md | 103 + .../40-otg-calculator-target.md | 133 + .../archive/math-tools-port/41-otg-driver.md | 136 + .../math-tools-port/42-otg-oracle-suites.md | 171 + .../math-tools-port/43-public-surface.md | 74 + .../44-otg-step2-udud-discriminant.md | 108 + .../45-otg-trivial-profile-length.md | 109 + .../46-otg-step1-sqrt-guard.md | 104 + ...ition-tolerance-and-timestamp-accessors.md | 107 + .../48-roots-degenerate-cases.md | 130 + .../49-waveform-spatial-pose-indexing.md | 112 + .../50-aggregate-container-api-idioms.md | 134 + ...51-waveform-generator-phase-conventions.md | 121 + .../52-subpackage-public-surface.md | 125 + .../53-window-convention-reconciliation.md | 134 + .../54-test-closure-tracks-bc.md | 185 + .../math-tools-port/55-test-closure-dsp.md | 229 + .../56-test-closure-otg-oracles.md | 274 + .../57-docs-and-convention-sweep.md | 266 + .claude/settings.local.json | 9 - .claude/specs/mathToolsArchitecture.md | 188 + .claude/specs/otg.md | 209 + .claude/specs/polynomials.md | 155 + .claude/specs/precisionTimeMath.md | 127 + .claude/specs/spatialMath.md | 192 + .claude/specs/templateConformance.md | 111 + .claude/specs/waveformCore.md | 240 + .claude/specs/waveformDsp.md | 314 + .editorconfig | 33 + .env | 23 +- .github/CODEOWNERS | 3 + .github/workflows/ci-cd.yml | 45 + .gitignore | 217 +- .pylintrc | 34 - .python-version | 1 + CODE_OF_CONDUCT.md | 3 +- CONTRIBUTING.md | 71 +- HISTORY.md | 18 +- LICENSE | 2 +- Makefile | 552 +- README.md | 103 +- examples/sphericalPlotting/plotArcs.py | 25 +- .../sphericalPlotting/plotQuatUnitCircles.py | 25 +- pyproject.toml | 86 +- requirements.txt | 22 + src/math_plot_helpers/__init__.py | 24 + .../plot_unit_spherical.py} | 295 +- .../py.typed} | 0 src/math_tools/__init__.py | 49 + src/math_tools/errors.py | 22 + src/math_tools/functional/__init__.py | 34 + src/math_tools/functional/polynomial.py | 182 + .../functional/py.typed} | 0 src/math_tools/functional/roots.py | 437 + src/{pyMathTools => math_tools}/hints.py | 19 +- src/math_tools/otg/__init__.py | 41 + src/math_tools/otg/block.py | 218 + src/math_tools/otg/bound.py | 37 + src/math_tools/otg/brake.py | 266 + src/math_tools/otg/calculator_target.py | 864 + src/math_tools/otg/enums.py | 124 + src/math_tools/otg/errors.py | 18 + src/math_tools/otg/input_parameter.py | 419 + src/math_tools/otg/otg.py | 182 + src/math_tools/otg/output_parameter.py | 89 + src/math_tools/otg/profile.py | 703 + .../__init__.py => math_tools/otg/py.typed} | 0 src/math_tools/otg/steps/__init__.py | 9 + .../otg/steps/position_first_order.py | 95 + .../otg/steps/position_second_order.py | 430 + .../otg/steps/position_third_order_step1.py | 1129 + .../otg/steps/position_third_order_step2.py | 2744 + .../otg/steps/velocity_second_order.py | 99 + .../otg/steps/velocity_third_order.py | 368 + src/math_tools/otg/trajectory.py | 325 + src/math_tools/precision_time/__init__.py | 9 + .../precision_time/precision_time_interval.py | 272 + .../precision_time/precision_timestamp.py | 413 + .../precision_time/py.typed} | 0 src/math_tools/py.typed | 0 src/math_tools/spatial/__init__.py | 11 + src/math_tools/spatial/position.py | 392 + src/math_tools/spatial/py.typed | 0 .../spatial/quaternion.py} | 187 +- src/math_tools/spatial/spatial_pose.py | 273 + src/math_tools/spherical/__init__.py | 32 + .../spherical/constructors.py | 10 +- src/math_tools/spherical/py.typed | 0 .../spherical/spherical_generators.py} | 35 +- .../spherical/spherical_transforms.py} | 29 +- src/math_tools/waveforms/__init__.py | 83 + src/math_tools/waveforms/dsp/__init__.py | 38 + src/math_tools/waveforms/dsp/_calc.py | 73 + src/math_tools/waveforms/dsp/_common.py | 32 + src/math_tools/waveforms/dsp/_correlation.py | 221 + src/math_tools/waveforms/dsp/_envelope.py | 170 + src/math_tools/waveforms/dsp/_filtering.py | 410 + src/math_tools/waveforms/dsp/_peaks.py | 149 + src/math_tools/waveforms/dsp/_phase.py | 249 + src/math_tools/waveforms/dsp/_protocol.py | 99 + src/math_tools/waveforms/dsp/_resampling.py | 258 + src/math_tools/waveforms/dsp/_spectral.py | 408 + .../waveforms/dsp/_time_alignment.py | 319 + src/math_tools/waveforms/dsp/_triggers.py | 396 + src/math_tools/waveforms/dsp/_windowing.py | 167 + .../waveforms/dsp/_zero_crossings.py | 213 + src/math_tools/waveforms/dsp/py.typed | 0 src/math_tools/waveforms/py.typed | 0 src/math_tools/waveforms/support.py | 389 + src/math_tools/waveforms/waveform1d.py | 1188 + src/math_tools/waveforms/waveform_position.py | 407 + .../waveforms/waveform_quaternion.py | 446 + .../waveforms/waveform_spatial_pose.py | 743 + tests/__init__.py | 2 +- tests/functional/__init__.py | 0 tests/functional/test_polynomial.py | 241 + tests/functional/test_roots.py | 355 + tests/otg/__init__.py | 0 tests/otg/data/failed_trajectories.json | 3878 + tests/otg/data/generate_multi_dof_corpus.py | 136 + tests/otg/data/otg_numeric_truth.json | 725 + tests/otg/data/successful_trajectories.json | 62236 ++++++++++++++++ .../successful_trajectories_multi_dof.json | 25652 +++++++ tests/otg/steps/__init__.py | 0 tests/otg/steps/test_position_first_second.py | 306 + tests/otg/steps/test_position_third_step1.py | 270 + tests/otg/steps/test_position_third_step2.py | 276 + tests/otg/steps/test_velocity_steps.py | 245 + tests/otg/test_block.py | 246 + tests/otg/test_brake.py | 195 + tests/otg/test_calculator_target.py | 585 + tests/otg/test_enums.py | 131 + tests/otg/test_input_parameter.py | 314 + tests/otg/test_otg_comprehensive.py | 216 + tests/otg/test_otg_continuity.py | 334 + tests/otg/test_otg_driver.py | 297 + tests/otg/test_otg_failure_fixes.py | 190 + tests/otg/test_otg_invariants.py | 257 + tests/otg/test_otg_truth_table.py | 172 + tests/otg/test_output_parameter.py | 111 + tests/otg/test_profile.py | 359 + tests/otg/test_trajectory.py | 179 + tests/precision_time/__init__.py | 0 .../test_precision_time_interval.py | 427 + .../test_precision_timestamp.py | 468 + tests/spatial/__init__.py | 0 tests/spatial/test_position.py | 501 + tests/spatial/test_quaternion_additions.py | 131 + tests/spatial/test_spatial_pose.py | 416 + tests/spherical/__init__.py | 0 tests/spherical/test_public_surface.py | 96 + tests/test_errors.py | 46 + tests/test_governance.py | 128 + tests/test_package_layering.py | 159 + tests/test_public_surface.py | 78 + tests/test_quaternion.py | 215 +- tests/waveforms/__init__.py | 0 tests/waveforms/dsp/__init__.py | 0 tests/waveforms/dsp/test_calc.py | 152 + tests/waveforms/dsp/test_compose.py | 147 + tests/waveforms/dsp/test_correlation.py | 241 + tests/waveforms/dsp/test_envelope.py | 263 + tests/waveforms/dsp/test_filtering.py | 552 + tests/waveforms/dsp/test_peaks.py | 234 + tests/waveforms/dsp/test_phase.py | 295 + tests/waveforms/dsp/test_resampling.py | 345 + tests/waveforms/dsp/test_spectral.py | 484 + tests/waveforms/dsp/test_time_alignment.py | 279 + tests/waveforms/dsp/test_triggers.py | 373 + tests/waveforms/dsp/test_windowing.py | 243 + tests/waveforms/dsp/test_zero_crossings.py | 198 + tests/waveforms/test_support.py | 216 + tests/waveforms/test_waveform1d_core.py | 633 + tests/waveforms/test_waveform1d_generators.py | 308 + tests/waveforms/test_waveform1d_operators.py | 400 + tests/waveforms/test_waveform_position.py | 479 + tests/waveforms/test_waveform_quaternion.py | 613 + tests/waveforms/test_waveform_spatial_pose.py | 787 + 219 files changed, 134400 insertions(+), 673 deletions(-) create mode 100644 .bumpversion.cfg create mode 100644 .claude/CLAUDE.md create mode 100644 .claude/archive/math-tools-port/00-overview.md create mode 100644 .claude/archive/math-tools-port/01-template-rename-and-refresh.md create mode 100644 .claude/archive/math-tools-port/02-errors-and-layering.md create mode 100644 .claude/archive/math-tools-port/03-governance-and-readme.md create mode 100644 .claude/archive/math-tools-port/04-precision-time-interval.md create mode 100644 .claude/archive/math-tools-port/05-precision-timestamp.md create mode 100644 .claude/archive/math-tools-port/06-position.md create mode 100644 .claude/archive/math-tools-port/07-quaternion-additions.md create mode 100644 .claude/archive/math-tools-port/08-spatial-pose.md create mode 100644 .claude/archive/math-tools-port/09-univariate-polynomial.md create mode 100644 .claude/archive/math-tools-port/10-roots-kernel.md create mode 100644 .claude/archive/math-tools-port/11-waveform1d-core.md create mode 100644 .claude/archive/math-tools-port/12-waveform1d-operators.md create mode 100644 .claude/archive/math-tools-port/13-waveform1d-generators.md create mode 100644 .claude/archive/math-tools-port/14-dsp-support-and-protocol.md create mode 100644 .claude/archive/math-tools-port/15-waveform-position.md create mode 100644 .claude/archive/math-tools-port/16-waveform-quaternion.md create mode 100644 .claude/archive/math-tools-port/17-waveform-spatial-pose.md create mode 100644 .claude/archive/math-tools-port/18-dsp-calc.md create mode 100644 .claude/archive/math-tools-port/19-dsp-correlation.md create mode 100644 .claude/archive/math-tools-port/20-dsp-envelope.md create mode 100644 .claude/archive/math-tools-port/21-dsp-spectral.md create mode 100644 .claude/archive/math-tools-port/22-dsp-filtering.md create mode 100644 .claude/archive/math-tools-port/23-dsp-peaks.md create mode 100644 .claude/archive/math-tools-port/24-dsp-phase.md create mode 100644 .claude/archive/math-tools-port/25-dsp-resampling.md create mode 100644 .claude/archive/math-tools-port/26-dsp-time-alignment.md create mode 100644 .claude/archive/math-tools-port/27-dsp-triggers.md create mode 100644 .claude/archive/math-tools-port/28-dsp-windowing.md create mode 100644 .claude/archive/math-tools-port/29-dsp-zero-crossings.md create mode 100644 .claude/archive/math-tools-port/30-dsp-compose.md create mode 100644 .claude/archive/math-tools-port/31-otg-enums-and-errors.md create mode 100644 .claude/archive/math-tools-port/32-otg-input-parameter.md create mode 100644 .claude/archive/math-tools-port/33-otg-profile.md create mode 100644 .claude/archive/math-tools-port/34-otg-block-brake-bound.md create mode 100644 .claude/archive/math-tools-port/35-otg-trajectory-and-output.md create mode 100644 .claude/archive/math-tools-port/36-otg-velocity-steps.md create mode 100644 .claude/archive/math-tools-port/37-otg-position-first-second-steps.md create mode 100644 .claude/archive/math-tools-port/38-otg-position-third-step1.md create mode 100644 .claude/archive/math-tools-port/39-otg-position-third-step2.md create mode 100644 .claude/archive/math-tools-port/40-otg-calculator-target.md create mode 100644 .claude/archive/math-tools-port/41-otg-driver.md create mode 100644 .claude/archive/math-tools-port/42-otg-oracle-suites.md create mode 100644 .claude/archive/math-tools-port/43-public-surface.md create mode 100644 .claude/archive/math-tools-port/44-otg-step2-udud-discriminant.md create mode 100644 .claude/archive/math-tools-port/45-otg-trivial-profile-length.md create mode 100644 .claude/archive/math-tools-port/46-otg-step1-sqrt-guard.md create mode 100644 .claude/archive/math-tools-port/47-position-tolerance-and-timestamp-accessors.md create mode 100644 .claude/archive/math-tools-port/48-roots-degenerate-cases.md create mode 100644 .claude/archive/math-tools-port/49-waveform-spatial-pose-indexing.md create mode 100644 .claude/archive/math-tools-port/50-aggregate-container-api-idioms.md create mode 100644 .claude/archive/math-tools-port/51-waveform-generator-phase-conventions.md create mode 100644 .claude/archive/math-tools-port/52-subpackage-public-surface.md create mode 100644 .claude/archive/math-tools-port/53-window-convention-reconciliation.md create mode 100644 .claude/archive/math-tools-port/54-test-closure-tracks-bc.md create mode 100644 .claude/archive/math-tools-port/55-test-closure-dsp.md create mode 100644 .claude/archive/math-tools-port/56-test-closure-otg-oracles.md create mode 100644 .claude/archive/math-tools-port/57-docs-and-convention-sweep.md delete mode 100644 .claude/settings.local.json create mode 100644 .claude/specs/mathToolsArchitecture.md create mode 100644 .claude/specs/otg.md create mode 100644 .claude/specs/polynomials.md create mode 100644 .claude/specs/precisionTimeMath.md create mode 100644 .claude/specs/spatialMath.md create mode 100644 .claude/specs/templateConformance.md create mode 100644 .claude/specs/waveformCore.md create mode 100644 .claude/specs/waveformDsp.md create mode 100644 .editorconfig create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/ci-cd.yml delete mode 100644 .pylintrc create mode 100644 .python-version create mode 100644 requirements.txt create mode 100644 src/math_plot_helpers/__init__.py rename src/{pyMathToolsPlotHelpers/plotUnitSpherical.py => math_plot_helpers/plot_unit_spherical.py} (78%) rename src/{pyMathTools/__init__.py => math_plot_helpers/py.typed} (100%) create mode 100644 src/math_tools/__init__.py create mode 100644 src/math_tools/errors.py create mode 100644 src/math_tools/functional/__init__.py create mode 100644 src/math_tools/functional/polynomial.py rename src/{pyMathTools/spatial/__init__.py => math_tools/functional/py.typed} (100%) create mode 100644 src/math_tools/functional/roots.py rename src/{pyMathTools => math_tools}/hints.py (57%) create mode 100644 src/math_tools/otg/__init__.py create mode 100644 src/math_tools/otg/block.py create mode 100644 src/math_tools/otg/bound.py create mode 100644 src/math_tools/otg/brake.py create mode 100644 src/math_tools/otg/calculator_target.py create mode 100644 src/math_tools/otg/enums.py create mode 100644 src/math_tools/otg/errors.py create mode 100644 src/math_tools/otg/input_parameter.py create mode 100644 src/math_tools/otg/otg.py create mode 100644 src/math_tools/otg/output_parameter.py create mode 100644 src/math_tools/otg/profile.py rename src/{pyMathTools/spherical/__init__.py => math_tools/otg/py.typed} (100%) create mode 100644 src/math_tools/otg/steps/__init__.py create mode 100644 src/math_tools/otg/steps/position_first_order.py create mode 100644 src/math_tools/otg/steps/position_second_order.py create mode 100644 src/math_tools/otg/steps/position_third_order_step1.py create mode 100644 src/math_tools/otg/steps/position_third_order_step2.py create mode 100644 src/math_tools/otg/steps/velocity_second_order.py create mode 100644 src/math_tools/otg/steps/velocity_third_order.py create mode 100644 src/math_tools/otg/trajectory.py create mode 100644 src/math_tools/precision_time/__init__.py create mode 100644 src/math_tools/precision_time/precision_time_interval.py create mode 100644 src/math_tools/precision_time/precision_timestamp.py rename src/{pyMathToolsPlotHelpers/__init__.py => math_tools/precision_time/py.typed} (100%) create mode 100644 src/math_tools/py.typed create mode 100644 src/math_tools/spatial/__init__.py create mode 100644 src/math_tools/spatial/position.py create mode 100644 src/math_tools/spatial/py.typed rename src/{pyMathTools/spatial/Quaternion.py => math_tools/spatial/quaternion.py} (82%) create mode 100644 src/math_tools/spatial/spatial_pose.py create mode 100644 src/math_tools/spherical/__init__.py rename src/{pyMathTools => math_tools}/spherical/constructors.py (93%) create mode 100644 src/math_tools/spherical/py.typed rename src/{pyMathTools/spherical/sphericalGenerators.py => math_tools/spherical/spherical_generators.py} (95%) rename src/{pyMathTools/spherical/sphericalTransforms.py => math_tools/spherical/spherical_transforms.py} (92%) create mode 100644 src/math_tools/waveforms/__init__.py create mode 100644 src/math_tools/waveforms/dsp/__init__.py create mode 100644 src/math_tools/waveforms/dsp/_calc.py create mode 100644 src/math_tools/waveforms/dsp/_common.py create mode 100644 src/math_tools/waveforms/dsp/_correlation.py create mode 100644 src/math_tools/waveforms/dsp/_envelope.py create mode 100644 src/math_tools/waveforms/dsp/_filtering.py create mode 100644 src/math_tools/waveforms/dsp/_peaks.py create mode 100644 src/math_tools/waveforms/dsp/_phase.py create mode 100644 src/math_tools/waveforms/dsp/_protocol.py create mode 100644 src/math_tools/waveforms/dsp/_resampling.py create mode 100644 src/math_tools/waveforms/dsp/_spectral.py create mode 100644 src/math_tools/waveforms/dsp/_time_alignment.py create mode 100644 src/math_tools/waveforms/dsp/_triggers.py create mode 100644 src/math_tools/waveforms/dsp/_windowing.py create mode 100644 src/math_tools/waveforms/dsp/_zero_crossings.py create mode 100644 src/math_tools/waveforms/dsp/py.typed create mode 100644 src/math_tools/waveforms/py.typed create mode 100644 src/math_tools/waveforms/support.py create mode 100644 src/math_tools/waveforms/waveform1d.py create mode 100644 src/math_tools/waveforms/waveform_position.py create mode 100644 src/math_tools/waveforms/waveform_quaternion.py create mode 100644 src/math_tools/waveforms/waveform_spatial_pose.py create mode 100644 tests/functional/__init__.py create mode 100644 tests/functional/test_polynomial.py create mode 100644 tests/functional/test_roots.py create mode 100644 tests/otg/__init__.py create mode 100644 tests/otg/data/failed_trajectories.json create mode 100644 tests/otg/data/generate_multi_dof_corpus.py create mode 100644 tests/otg/data/otg_numeric_truth.json create mode 100644 tests/otg/data/successful_trajectories.json create mode 100644 tests/otg/data/successful_trajectories_multi_dof.json create mode 100644 tests/otg/steps/__init__.py create mode 100644 tests/otg/steps/test_position_first_second.py create mode 100644 tests/otg/steps/test_position_third_step1.py create mode 100644 tests/otg/steps/test_position_third_step2.py create mode 100644 tests/otg/steps/test_velocity_steps.py create mode 100644 tests/otg/test_block.py create mode 100644 tests/otg/test_brake.py create mode 100644 tests/otg/test_calculator_target.py create mode 100644 tests/otg/test_enums.py create mode 100644 tests/otg/test_input_parameter.py create mode 100644 tests/otg/test_otg_comprehensive.py create mode 100644 tests/otg/test_otg_continuity.py create mode 100644 tests/otg/test_otg_driver.py create mode 100644 tests/otg/test_otg_failure_fixes.py create mode 100644 tests/otg/test_otg_invariants.py create mode 100644 tests/otg/test_otg_truth_table.py create mode 100644 tests/otg/test_output_parameter.py create mode 100644 tests/otg/test_profile.py create mode 100644 tests/otg/test_trajectory.py create mode 100644 tests/precision_time/__init__.py create mode 100644 tests/precision_time/test_precision_time_interval.py create mode 100644 tests/precision_time/test_precision_timestamp.py create mode 100644 tests/spatial/__init__.py create mode 100644 tests/spatial/test_position.py create mode 100644 tests/spatial/test_quaternion_additions.py create mode 100644 tests/spatial/test_spatial_pose.py create mode 100644 tests/spherical/__init__.py create mode 100644 tests/spherical/test_public_surface.py create mode 100644 tests/test_errors.py create mode 100644 tests/test_governance.py create mode 100644 tests/test_package_layering.py create mode 100644 tests/test_public_surface.py create mode 100644 tests/waveforms/__init__.py create mode 100644 tests/waveforms/dsp/__init__.py create mode 100644 tests/waveforms/dsp/test_calc.py create mode 100644 tests/waveforms/dsp/test_compose.py create mode 100644 tests/waveforms/dsp/test_correlation.py create mode 100644 tests/waveforms/dsp/test_envelope.py create mode 100644 tests/waveforms/dsp/test_filtering.py create mode 100644 tests/waveforms/dsp/test_peaks.py create mode 100644 tests/waveforms/dsp/test_phase.py create mode 100644 tests/waveforms/dsp/test_resampling.py create mode 100644 tests/waveforms/dsp/test_spectral.py create mode 100644 tests/waveforms/dsp/test_time_alignment.py create mode 100644 tests/waveforms/dsp/test_triggers.py create mode 100644 tests/waveforms/dsp/test_windowing.py create mode 100644 tests/waveforms/dsp/test_zero_crossings.py create mode 100644 tests/waveforms/test_support.py create mode 100644 tests/waveforms/test_waveform1d_core.py create mode 100644 tests/waveforms/test_waveform1d_generators.py create mode 100644 tests/waveforms/test_waveform1d_operators.py create mode 100644 tests/waveforms/test_waveform_position.py create mode 100644 tests/waveforms/test_waveform_quaternion.py create mode 100644 tests/waveforms/test_waveform_spatial_pose.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..54b6c97 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,10 @@ +[bumpversion] +current_version = 0.0.2 +commit = true +tag = false +tag_name = v{new_version} +message = Bump version: {current_version} → {new_version} + +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" \ No newline at end of file diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..f1ef708 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,78 @@ +--- +last_updated: 2026-07-23 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`py-MathTools` is the **Tier 3 math implementation layer** of the foundation +math tiers defined in py-foundationTools' `mathTypeTiers.md`: it subclasses +the Tier-2 `foundation_abc.math.*` ABCs, chooses numpy-backed storage, and +implements arithmetic, composition, interpolation, DSP, and trajectory +generation on top of the Tier-1 `foundationTypes.mathTypes` data carriers. +Unlike `pyFoundationTools` (zero-dependency by policy), this repo depends on +a curated set of mature numeric packages rather than reimplementing them. + +## Packages and layering + +Two top-level snake_case packages under `src/`, one-way dependency: + +``` +math_plot_helpers → math_tools → pyFoundationTools → stdlib + (matplotlib) (numpy, scipy, numpy-quaternion) +``` + +`math_tools` never imports `math_plot_helpers` or `matplotlib`; only +`math_plot_helpers` imports `matplotlib`. This is enforced by +`tests/test_package_layering.py` (static AST scan of `src/`). + +## Commands + +All workflows go through the Makefile (`make help` lists them); **the +Makefile is the source of truth for tooling**, not this file or the README. +The `uv-` prefixed targets are the primary path. Key ones: + +- `make uv-fullCheck` — CI gate: `uv-lint` + `uv-typecheck` + `uv-test`. Run + this before considering work done. +- `make uv-lint` — ruff check +- `make uv-format` — `ruff format` + `ruff check --fix --unsafe-fixes` +- `make uv-typecheck` — strict `mypy` over `src/` + `tests/` +- `make uv-test` — sync deps then run pytest +- `make uv-refresh` — clean cache + reinstall from `requirements.txt` + + upgrade editable dev install (required after a `pyFoundationTools` pin + changes; a stale `.venv` makes the gate meaningless) + +## Specs + +`.claude/specs/` is the authoritative contract for this repo's architecture +and every module's behavior. Consult the relevant spec before extending a +module; if implementation forces a contract change, the spec is updated in +the same change and its `semver` bumped. + +- [mathToolsArchitecture.md](specs/mathToolsArchitecture.md) — umbrella: + layering, package layout, dependency policy, shared conventions, error + semantics +- [templateConformance.md](specs/templateConformance.md) — template + migration: packaging, Makefile/CI parity, rename, governance docs +- [precisionTimeMath.md](specs/precisionTimeMath.md) — `PrecisionTimeInterval`, + `PrecisionTimestamp` +- [spatialMath.md](specs/spatialMath.md) — `Position`, `Quaternion`, + `SpatialPose` +- [waveformCore.md](specs/waveformCore.md) — `Waveform1D` + aggregate + waveform containers +- [waveformDsp.md](specs/waveformDsp.md) — DSP families, scipy mapping, + support types +- [polynomials.md](specs/polynomials.md) — polynomial type + analytic root + solvers +- [otg.md](specs/otg.md) — online trajectory generation (Ruckig port) + +## Tests + +Tests live in `tests/`, `unittest.TestCase` style run under pytest +(`test*.py` files, `test_*` methods), mirroring the package layout under +`src/` (e.g. `tests/spatial/test_position.py`). diff --git a/.claude/archive/math-tools-port/00-overview.md b/.claude/archive/math-tools-port/00-overview.md new file mode 100644 index 0000000..e820701 --- /dev/null +++ b/.claude/archive/math-tools-port/00-overview.md @@ -0,0 +1,195 @@ +--- +plan: math-tools-port +status: complete +last_updated: 2026-07-23 +semver: 0.1.2 +author: Nicholas Bergantz +--- + +# Action Plan — Template Migration + Swift Math Port + +**Goal:** bring py-MathTools onto the py-foundationTools template conventions +and port the Swift `FoundationMathTypes` capability set (spatial SE(3) types, +precision time, waveforms + DSP, polynomials/roots, OTG trajectory +generation) as the Tier-3 math layer, per the accepted specs in +[`../specs/`](../specs/mathToolsArchitecture.md). + +**Usability north star:** a robotics/DSP engineer in a notebook can build a +waveform or pose, do the obvious math, and reach scipy-grade analysis with +minimal ceremony — well-typed numpy-adjacent Python, not translated Swift. + +## Conventions every chunk inherits (do not restate per chunk) + +1. **Spec is authoritative.** Each chunk lists its governing spec section(s). + If implementation forces a contract change, update the spec in the same + chunk and bump its `semver`. +2. **TDD:** write the failing tests first, implement, then run the gate. +3. **Gate:** `make uv-fullCheck` (ruff lint + mypy strict + pytest) must pass + at the end of every chunk. Test layout mirrors the package + (`tests//test_.py`), `unittest.TestCase` style, `test_*` + methods. +4. **Stay in scope:** touch ONLY the files the chunk lists. Adjacent + problems get reported in the chunk's completion notes, not fixed. +5. **Frontmatter:** every chunk carries `status: pending` → set + `in_progress` while working, then `complete` when done (not `done` + — `complete` is the convention actually used across every chunk file + in this repo); bump `last_updated`. +6. **Swift reference roots** (read-only, for faithful-port chunks): + - `SWIFT_MATH` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmMathTools/spm/Sources/spmMathTools/FoundationMathTypes` + - `SWIFT_TYPES` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmFoundationTools/spm/Sources/FoundationTypes` + - `SWIFT_TESTS` = `/Users/nbergantz/__Workspaces__/spmWorkspaces/spmMathTools/spm/Tests/spmMathToolsTests` +7. **Shared API idioms** (umbrella spec "API idioms"): `normalized()` method + / `normalize()` in-place; `isclose(rtol, atol)`; `__array__`; + `__hash__ = None` on mutable numpy-backed classes; snake_case throughout. +8. Do not commit; the human reviews and commits per track. + +## Dependency graph + +``` +Track A (template) 01 ──► 02 ──► 03 + │ + ┌────────────────┴───────────────────────────────┐ +Track B │ 04 ─► 05 06 ─► 07 ─► 08 09 10 │ (02 before all B) + │ └──────┬──────────┘└───┬────┘ │ │ +Track C │ ▼ │ │ │ + │ 11 ─► {12, 13, 14} │ │ │ + │ │ │ ▼ │ │ + │ │ │ 15(◄06) 16(◄07) ─► 17(◄08,15,16)│ +Track D │ │ ▼ │ │ + │ │ 18..29 (one per mixin; 26 also ◄19) │ │ + │ │ └────────► 30 (compose) │ │ +Track E │ └──────────────────────────────────────────┘ │ + │ 31(◄10) ─► 32 │ + │ 31 ─► 33 ─► 34 ─► {35, 36, 37, 38, 39} │ + │ {32,35..39} ─► 40 ─► 41 ─► 42 │ + └────────────────────────────────────────────────┘ +``` + +Tracks B/C/D/E parallelize after 01–02; within a track, run in numeric +order unless the graph says otherwise. Chunk 43 (public surface) runs last, +after 09, 17, 30, and 42. + +## Chunk index + +| # | Chunk | Track | Depends on | Spec | +|---|---|---|---|---| +| 01 | [template-rename-and-refresh](01-template-rename-and-refresh.md) | A | — | templateConformance | +| 02 | [errors-and-layering](02-errors-and-layering.md) | A | 01 | umbrella, templateConformance §5 | +| 03 | [governance-and-readme](03-governance-and-readme.md) | A | 02 | templateConformance §3–4 | +| 04 | [precision-time-interval](04-precision-time-interval.md) | B | 02 | precisionTimeMath | +| 05 | [precision-timestamp](05-precision-timestamp.md) | B | 04 | precisionTimeMath | +| 06 | [position](06-position.md) | B | 02 | spatialMath | +| 07 | [quaternion-additions](07-quaternion-additions.md) | B | 06 | spatialMath | +| 08 | [spatial-pose](08-spatial-pose.md) | B | 07 | spatialMath | +| 09 | [univariate-polynomial](09-univariate-polynomial.md) | B | 02 | polynomials | +| 10 | [roots-kernel](10-roots-kernel.md) | B | 02 | polynomials | +| 11 | [waveform1d-core](11-waveform1d-core.md) | C | 05 | waveformCore | +| 12 | [waveform1d-operators](12-waveform1d-operators.md) | C | 11 | waveformCore | +| 13 | [waveform1d-generators](13-waveform1d-generators.md) | C | 11 | waveformCore | +| 14 | [dsp-support-and-protocol](14-dsp-support-and-protocol.md) | C | 11 | waveformDsp | +| 15 | [waveform-position](15-waveform-position.md) | C | 11, 06 | waveformCore | +| 16 | [waveform-quaternion](16-waveform-quaternion.md) | C | 11, 07 | waveformCore | +| 17 | [waveform-spatial-pose](17-waveform-spatial-pose.md) | C | 08, 15, 16 | waveformCore | +| 18 | [dsp-calc](18-dsp-calc.md) | D | 14 | waveformDsp | +| 19 | [dsp-correlation](19-dsp-correlation.md) | D | 14 | waveformDsp | +| 20 | [dsp-envelope](20-dsp-envelope.md) | D | 14 | waveformDsp | +| 21 | [dsp-spectral](21-dsp-spectral.md) | D | 14 | waveformDsp | +| 22 | [dsp-filtering](22-dsp-filtering.md) | D | 14 | waveformDsp | +| 23 | [dsp-peaks](23-dsp-peaks.md) | D | 14 | waveformDsp | +| 24 | [dsp-phase](24-dsp-phase.md) | D | 14 | waveformDsp | +| 25 | [dsp-resampling](25-dsp-resampling.md) | D | 14 | waveformDsp | +| 26 | [dsp-time-alignment](26-dsp-time-alignment.md) | D | 14, 19 | waveformDsp | +| 27 | [dsp-triggers](27-dsp-triggers.md) | D | 14 | waveformDsp | +| 28 | [dsp-windowing](28-dsp-windowing.md) | D | 14 | waveformDsp | +| 29 | [dsp-zero-crossings](29-dsp-zero-crossings.md) | D | 14 | waveformDsp | +| 30 | [dsp-compose](30-dsp-compose.md) | D | 18–29 | waveformDsp | +| 31 | [otg-enums-and-errors](31-otg-enums-and-errors.md) | E | 10 | otg | +| 32 | [otg-input-parameter](32-otg-input-parameter.md) | E | 31 | otg | +| 33 | [otg-profile](33-otg-profile.md) | E | 31 | otg | +| 34 | [otg-block-brake-bound](34-otg-block-brake-bound.md) | E | 33 | otg | +| 35 | [otg-trajectory-and-output](35-otg-trajectory-and-output.md) | E | 34 | otg | +| 36 | [otg-velocity-steps](36-otg-velocity-steps.md) | E | 34 | otg | +| 37 | [otg-position-first-second-steps](37-otg-position-first-second-steps.md) | E | 34 | otg | +| 38 | [otg-position-third-step1](38-otg-position-third-step1.md) | E | 34 | otg | +| 39 | [otg-position-third-step2](39-otg-position-third-step2.md) | E | 34 | otg | +| 40 | [otg-calculator-target](40-otg-calculator-target.md) | E | 32, 35–39 | otg | +| 41 | [otg-driver](41-otg-driver.md) | E | 40 | otg | +| 42 | [otg-oracle-suites](42-otg-oracle-suites.md) | E | 41 | otg | +| 43 | [public-surface](43-public-surface.md) | A | 30, 42, 09, 17 | umbrella | + +--- + +# Corrective actions — 2026-07-22 post-audit + +Chunks 01–43 were audited against their governing specs by five parallel +skeptical auditors (one per track), with the top findings independently +reproduced at runtime and source level before being recorded here. + +**Verdict: substantially complete.** `make uv-fullCheck` is green (exit 0) and +the port is faithful in the large — the 31-case OTG numeric truth table was +re-derived from the Swift source with 0 mismatches, no test in the repo is +skipped or xfail'd, and no unsolved algorithm case, stub, or swallowed `None` +was found in the solvers. The confirmed gaps are specific and are chunked below. + +Chunks 44–57 form **Track F**. They are corrective, not new capability. + +## Confirmed defect summary + +| Class | Count | Where | +|---|---|---| +| (c) drift / correctness | 8 | OTG solvers, spatial/time accessors, waveform indexing | +| (a) not implemented | 6 | aggregate API idioms, subpackage exports, roots degeneracy | +| (b) implemented, untested | ~45 | concentrated in DSP mixins and OTG oracles | +| (e) docs/convention drift | 14 | dead workflow refs, stale tolerances, boilerplate | +| (d) out-of-scope violations | **0** | — none found in any track | + +The single most consequential finding is a **transcription defect** at +`position_third_order_step2.py:1731,1752,1763` (`j·tf⁴` where Swift has +`j·tf³`), confirmed by direct comparison against +`PositionThirdOrderStep2.swift:1279,1286,1289`. It changes profile-branch +selection in ~0.57% of prescribed-duration Step2 solves and is invisible to the +current gate — because the entire 1,784-case OTG oracle corpus is 1-DOF, which +takes a fast path that **never invokes Step2 at all** (measured: 0 invocations). + +## Dependency graph + +``` +Track F (corrective) + + OTG code fixes 44 45 46 + └────┴────┴──────► 56 (OTG oracle closure) + + Track B/C code fixes 47 48 49 ─► 50 51 + └────┴────┴─► 54 (B/C test closure) + + Surface / conventions 52 53 ─────────────► 55 (DSP test closure) + + Everything above ───────────────────────────────► 57 (docs sweep, last) +``` + +44–49, 51, 52, 53 are mutually independent and may run in parallel. +50 waits on 49. The three closure chunks wait on their code fixes. 57 runs last +so it reconciles docs against the post-fix repo. + +## Corrective chunk index + +| # | Chunk | Kind | Depends on | Origin | +|---|---|---|---|---| +| 44 | [otg-step2-udud-discriminant](44-otg-step2-udud-discriminant.md) | fix | — | E-1 | +| 45 | [otg-trivial-profile-length](45-otg-trivial-profile-length.md) | fix | — | E-2 | +| 46 | [otg-step1-sqrt-guard](46-otg-step1-sqrt-guard.md) | fix | — | E-3 | +| 47 | [position-tolerance-and-timestamp-accessors](47-position-tolerance-and-timestamp-accessors.md) | fix | — | B-1, B-2 | +| 48 | [roots-degenerate-cases](48-roots-degenerate-cases.md) | fix | — | B-7, B-14 | +| 49 | [waveform-spatial-pose-indexing](49-waveform-spatial-pose-indexing.md) | fix | — | C-4, C-5 | +| 50 | [aggregate-container-api-idioms](50-aggregate-container-api-idioms.md) | fix | 49 | C-1, C-2, C-3, C-7 | +| 51 | [waveform-generator-phase-conventions](51-waveform-generator-phase-conventions.md) | fix | — | C-15 | +| 52 | [subpackage-public-surface](52-subpackage-public-surface.md) | fix | — | A-1, A-2 | +| 53 | [window-convention-reconciliation](53-window-convention-reconciliation.md) | fix | — | D-windowing | +| 54 | [test-closure-tracks-bc](54-test-closure-tracks-bc.md) | tests | 47–51 | 11 (b) findings | +| 55 | [test-closure-dsp](55-test-closure-dsp.md) | tests | 53 | 23 (b) findings | +| 56 | [test-closure-otg-oracles](56-test-closure-otg-oracles.md) | tests | 44, 45, 46 | 8 (b) findings | +| 57 | [docs-and-convention-sweep](57-docs-and-convention-sweep.md) | docs | 44–56 | 14 (e) findings | + +Findings deliberately **not** actioned (B-4, B-5, B-6, A-7, D-cross-cutting, +D-spectral-(c), E-10) are recorded with rationale in chunk 57's +"Recorded — no action" section, so the decisions are not relitigated. diff --git a/.claude/archive/math-tools-port/01-template-rename-and-refresh.md b/.claude/archive/math-tools-port/01-template-rename-and-refresh.md new file mode 100644 index 0000000..2d09849 --- /dev/null +++ b/.claude/archive/math-tools-port/01-template-rename-and-refresh.md @@ -0,0 +1,123 @@ +--- +chunk: 01-template-rename-and-refresh +track: A +status: complete +depends_on: [] +spec: ../specs/templateConformance.md §Gap 1, §Gap 2.4; ../specs/spatialMath.md §Modules (ABC re-parent) +last_updated: 2026-07-11 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# 01 — Package rename + environment refresh + +**Deliverable:** the repo builds and gates green under the new snake_case +package names against the pinned foundation branch. Mechanical migration — +no behavior changes beyond the required ABC re-parent. + +## Files + +- `git mv src/pyMathTools src/math_tools`; inside it: + `git mv src/math_tools/spatial/Quaternion.py src/math_tools/spatial/quaternion.py`, + `git mv src/math_tools/spherical/sphericalGenerators.py src/math_tools/spherical/spherical_generators.py`, + `git mv src/math_tools/spherical/sphericalTransforms.py src/math_tools/spherical/spherical_transforms.py` + (`constructors.py`, `hints.py` keep their names). +- `git mv src/pyMathToolsPlotHelpers src/math_plot_helpers`; + `git mv src/math_plot_helpers/plotUnitSpherical.py src/math_plot_helpers/plot_unit_spherical.py`. +- Edit: `src/math_tools/spatial/quaternion.py` (imports/base only, see below), + every `__init__.py` touched by moves, `tests/test_quaternion.py`, + `examples/sphericalPlotting/plotArcs.py`, + `examples/sphericalPlotting/plotQuatUnitCircles.py`, `Makefile` (mypy + package list if it names packages), `.env` if it names packages. +- Add: `py.typed` in `src/math_tools/` and `src/math_plot_helpers/` roots + (keep the existing ones in subpackages). + +## Design constraints + +1. **First action:** `make uv-refresh` so `.venv` matches the + `requirements.txt` branch pin (the stale install has the pre-template ABC + layout; nothing imports correctly until this runs). +2. **ABC re-parent (the one semantic edit):** in `quaternion.py`, replace + `from foundationTypes.mathTypes.quaternionABC import QuaternionABC` with + `from foundation_abc.math.spatialABCs import QuaternionABC`. The old ABC + carried `DataModelHelper`; the new one is ABC-only with a concrete + `to_dict`. In `tests/test_quaternion.py`, update the serialization tests' + base-class assertions (`DataModelHelper` inheritance assertion → the new + ABC) — assertions on `to_dict`/`from_dict` *values* stay untouched. If any + other import from the old layout exists (grep `foundationTypes.mathTypes.` + across `src/`), re-point to `foundation_abc.math.*` / + `foundationTypes.mathTypes.MathTypes` equivalents. +3. All other edits are import-path text substitutions + (`pyMathTools` → `math_tools`, `pyMathToolsPlotHelpers` → + `math_plot_helpers`, moved module filenames). +4. `pyproject.toml`: update `description` only if trivially co-located; the + deps list changes in chunk 03, not here. + +## TDD steps + +1. `make uv-refresh`; run `make uv-test` to record the pre-existing pass/fail + baseline (the ABC import may already be broken — note it). +2. Perform moves + edits. +3. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] `grep -rn "pyMathTools" src/ tests/ examples/ Makefile .env pyproject.toml` → no hits +- [x] `grep -rn "foundationTypes.mathTypes.quaternionABC" src/ tests/` → no hits +- [x] `git log --follow --oneline src/math_tools/spatial/quaternion.py` shows history (moves were `git mv`) — verified via `git status`/`git diff --staged` showing `renamed: src/pyMathTools/spatial/Quaternion.py -> src/math_tools/spatial/quaternion.py`; `--follow` itself needs a commit to walk, which this chunk intentionally leaves to the supervising process +- [x] All ~90 quaternion tests pass (89 collected/passed); diff to `tests/test_quaternion.py` contains only import lines, base-class assertion lines, and their two adjacent docstring lines (see Resolution notes) +- [x] `make uv-fullCheck` passes + +## Out of scope + +`errors.py`, layering test, README, `.claude/CLAUDE.md`, pyproject dependency +list, any new math code, any `__init__.py` re-export curation beyond fixing +broken imports. + +## Resolution notes + +- `make uv-refresh` pulled the pinned foundation branch fresh; pre-move + `make uv-test` baseline reproduced the expected pre-existing break + (`ModuleNotFoundError: foundationTypes.mathTypes.quaternionABC`), confirming + the venv was stale before this chunk and the gate is meaningful after. +- Moves done via `git mv` as specified. `git status`/`git diff --staged` + correctly report `quaternion.py` as a rename from `Quaternion.py`; git's + similarity heuristic cross-matched some of the (byte-identical, empty) + `__init__.py` files to different-but-equivalent old empty `__init__.py` + paths — cosmetic only, every file's on-disk destination was verified + directly with `find`, and it does not affect `--follow` on the files that + matter (confirmed for `quaternion.py`). +- The old-layout ABC repoint (design constraint 2) turned out to reach beyond + `quaternion.py`: `foundationTypes.mathTypes.unitSphericalArcABC` and + `unitSphericalSmallCircleABC` (imported by `spherical_generators.py`, + `spherical_transforms.py`, `constructors.py`, `plot_unit_spherical.py`) + were also deleted from the pinned foundation branch and now live at + `foundation_abc.math.sphericalABCs`. Re-pointed all of them per the chunk's + explicit "grep `foundationTypes.mathTypes.` across `src/`" instruction — + required for `make uv-fullCheck` (mypy strict) to pass, since mypy scans + all of `src/`, not just the quaternion module. `foundationTypes.mathTypes.MathTypes.*` + imports (the codegen `*Type` classes) were left untouched — that module + still exists unchanged in the new layout. + This is a scope note, not a spec deviation: constraint 2's own text + authorized exactly this action; the "Files" list section above just didn't + enumerate every file it touched. +- `examples/sphericalPlotting/plotArcs.py` and `plotQuatUnitCircles.py` got + only the mechanical package-name substitution, per constraint 3 and Gap 4's + explicit ownership of their pre-existing broken `foundationTypes.mathTypes.UnitSphericalArc` + / `UnitSphericalSmallCircle` imports (not part of this chunk's scope, and + not scanned by `make uv-typecheck` since `PY_EXAMPLES` is unset in `.env`). +- `tests/test_quaternion.py`: `test_inheritance_from_data_model_helper` + asserted `isinstance(q, DataModelHelper)`, which is no longer true (the new + `QuaternionABC` is ABC-only). Repointed the import and assertion to + `QuaternionABC` per constraint 2's directive, and updated that test's and + the class's docstrings by one line each so the docstrings don't contradict + the assertion right below them — the only lines in this diff beyond raw + import-path substitution. No `to_dict`/`from_dict` value assertions were + touched. +- Added `py.typed` at `src/math_tools/` and `src/math_plot_helpers/` package + roots (subpackage ones already existed). +- No `Makefile`/`.env` edits were needed — neither names packages explicitly + (both scope quality targets via path variables, not package names). +- `pyproject.toml` `description` left untouched — no trivially co-located + edit was applicable in this chunk; still boilerplate text, tracked as Gap 4 + (chunk 03). diff --git a/.claude/archive/math-tools-port/02-errors-and-layering.md b/.claude/archive/math-tools-port/02-errors-and-layering.md new file mode 100644 index 0000000..78fdbfb --- /dev/null +++ b/.claude/archive/math-tools-port/02-errors-and-layering.md @@ -0,0 +1,74 @@ +--- +chunk: 02-errors-and-layering +track: A +status: complete +depends_on: [01] +spec: ../specs/mathToolsArchitecture.md §Error semantics; ../specs/templateConformance.md §Gap 5 +last_updated: 2026-07-11 +semver: 0.0.1 +author: Nicholas Bergantz +--- + +# 02 — Exception hierarchy + package layering test + +**Deliverable:** `math_tools/errors.py` and the AST-based layering test. + +## Files + +- Create: `src/math_tools/errors.py` +- Create: `tests/test_package_layering.py` +- Create: `tests/test_errors.py` + +## Design constraints + +1. `errors.py` defines exactly (each with a one-line docstring): + `MathToolsError(Exception)`, `WaveformCompatibilityError(MathToolsError)`, + `TimestampComparisonError(MathToolsError)`, + `PolynomialSolveError(MathToolsError)`. +2. Layering test pattern: copy the approach of py-foundationTools + `tests/test_package_layering.py` (AST-walk every module under `src/`, + collect `import`/`from` roots). Assertions per templateConformance §Gap 5: + `math_tools` imports neither `math_plot_helpers` nor `matplotlib`; + only `math_plot_helpers` imports `matplotlib`. +3. Additionally assert `math_tools.otg` (once it exists) does not import + `numpy` — write the rule now, guarded to skip if the package dir is + absent, so OTG chunks inherit enforcement for free. + +## TDD steps + +1. Write `tests/test_errors.py` (hierarchy, catchability as `MathToolsError`) + and `tests/test_package_layering.py`; watch errors test fail. +2. Implement `errors.py`; layering test must pass against the current tree. +3. Temporarily add `import matplotlib` to a `math_tools` module and confirm + the layering test fails; revert. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] All four exception classes exist and subclass as specified +- [x] Layering test fails on an injected `import matplotlib` in `math_tools` (verified then reverted) +- [x] `make uv-fullCheck` passes + +## Out of scope + +Any consumer of the exceptions; OTG's `OtgError` (lives in `otg/errors.py`, +chunk 31); README/CLAUDE.md. + +## Resolution notes + +- `errors.py` implements exactly the four classes from the spec, each a + one-line docstring, no added behavior. +- `test_package_layering.py` follows the py-foundationTools AST-scan pattern + (`tests/test_package_layering.py` there) rather than executing imports, so + it can't be defeated by import side effects. Four checks: `math_tools` ↛ + `math_plot_helpers`, `math_tools` ↛ `matplotlib`, only `math_plot_helpers` + → `matplotlib` (scans all of `src/` excluding that package), and the + guarded `math_tools.otg` ↛ `numpy` rule (currently a no-op skip since + `otg/` doesn't exist yet — will activate automatically once chunk 31 lands). +- Verification step 3 (inject `import matplotlib` into `math_tools/errors.py`, + confirm two layering assertions fail, revert) was done live against the + actual gate, not simulated; file diffed back to the original after. +- One ruff fix needed: the injected-violation assertion message exceeded the + 100-char line limit in `test_only_math_plot_helpers_imports_matplotlib`'s + sibling test; wrapped the f-string across two lines. +- No spec changes were required — the chunk's design constraints matched the + umbrella spec's Error semantics section exactly. diff --git a/.claude/archive/math-tools-port/03-governance-and-readme.md b/.claude/archive/math-tools-port/03-governance-and-readme.md new file mode 100644 index 0000000..804cdab --- /dev/null +++ b/.claude/archive/math-tools-port/03-governance-and-readme.md @@ -0,0 +1,106 @@ +--- +chunk: 03-governance-and-readme +track: A +status: complete +depends_on: [02] +spec: ../specs/templateConformance.md §Gap 2, §Gap 3, §Gap 4 +last_updated: 2026-07-11 +semver: 0.0.2 +author: Nicholas Bergantz +--- + +# 03 — Governance docs, README, dependency names + +**Deliverable:** `.claude/CLAUDE.md`, real README, pyproject dependency +names, fixed examples. + +## Files + +- Create: `.claude/CLAUDE.md` +- Edit: `README.md`, `pyproject.toml` (deps + description only), + `examples/sphericalPlotting/plotArcs.py`, + `examples/sphericalPlotting/plotQuatUnitCircles.py` + +## Design constraints + +1. `.claude/CLAUDE.md` follows the shape of py-foundationTools + `.claude/CLAUDE.md` but stays short: repo role (Tier 3 of foundation's + `mathTypeTiers.md`), the two-package layering diagram, gate command, + Makefile-is-source-of-truth note, and a linked index of every spec in + `.claude/specs/`. Reference specs; never duplicate their content. +2. `pyproject.toml` `dependencies` = names only: + `pyFoundationTools`, `numpy`, `scipy`, `numpy-quaternion`, `matplotlib`. + Replace the boilerplate `description`. +3. README per templateConformance Gap 4: describe only what exists at + execution time (quaternion, spherical utilities, template workflows); + sections: title → Features → Installation → Quick Start → Development + Workflows → Requirements. Quick Start snippets must actually run. +4. `plotArcs.py`: fix the dead + `foundationTypes.mathTypes.UnitSphericalArc.UnitSphericalArc` import to + the real generated type in `foundationTypes.mathTypes.MathTypes` + (verify the class name by reading that module). + +## TDD steps + +1. Add a test `tests/test_governance.py`: `.claude/CLAUDE.md` exists and its + text links every `*.md` in `.claude/specs/`; `README.md` contains no + "Boilerplate". Watch it fail. +2. Write the docs; run both example scripts manually + (`uv run python examples/sphericalPlotting/plotArcs.py` with a + non-interactive matplotlib backend) to prove imports resolve. +3. `make uv-fullCheck` green. + +## Acceptance criteria + +- [x] `tests/test_governance.py` passes +- [x] `pyproject.toml` deps are exactly the five names, unpinned +- [x] Both example scripts import-run without error +- [x] `make uv-fullCheck` passes + +## Out of scope + +Makefile/CI edits; requirements pin changes; any `src/` code beyond the +example imports. + +## Resolution notes + +- `tests/test_governance.py` added: asserts `.claude/CLAUDE.md` exists and + its text contains the filename of every `.claude/specs/*.md` file, and + that `README.md` contains no `"Boilerplate"` text. Confirmed it failed + before the docs existed, passed after. +- `.claude/CLAUDE.md` written to the shape of py-foundationTools' + `.claude/CLAUDE.md` (Project Overview → layering → Commands → Specs index + → Tests) but scoped to what exists in this repo today: Tier 3 role, the + two-package layering diagram, the gate command, a Makefile-is-source-of- + truth note, and a linked index of all 7 specs in `.claude/specs/`. +- `README.md` rewritten per Gap 4's section shape (title → Features → + Installation → Quick Start → Development Workflows → Requirements), + describing only what exists at execution time: `Quaternion`, the + spherical arc/small-circle utilities, `errors.py`, and the + `math_plot_helpers` plotting package. Both Quick Start snippets + (quaternion arithmetic/conversion, spherical arc construction + + endpoint) were executed directly to confirm they run as written. +- `pyproject.toml`: `dependencies` set to the five names + (`pyFoundationTools`, `numpy`, `scipy`, `numpy-quaternion`, `matplotlib`) + — the prior list was missing `numpy`/`scipy` and had an unrelated stray + order; `description` replaced with a one-line Tier-3 summary. +- `examples/sphericalPlotting/plotArcs.py`: fixed the dead + `foundationTypes.mathTypes.UnitSphericalArc.UnitSphericalArc` import to + `foundationTypes.mathTypes.MathTypes.UnitSphericalArcType` (verified the + real class name by reading the installed `MathTypes.py`) and updated the + two local usages/type hints accordingly. +- `examples/sphericalPlotting/plotQuatUnitCircles.py`: same dead-import + pattern existed for `UnitSphericalSmallCircle` (not called out by name in + the chunk's design constraint 4, but the file was listed for edit and the + acceptance criterion requires *both* scripts to import-run without + error) — fixed to `foundationTypes.mathTypes.MathTypes.UnitSphericalSmallCircleType`. +- Both example scripts verified to run end-to-end with + `MPLBACKEND=Agg PYTHONPATH=src .venv/bin/python examples/sphericalPlotting/