Skip to content

feat(traits): add ControlDependence family + refactor strict-contract helpers - #471

Merged
ocots merged 2 commits into
mainfrom
feat/control-dependence-trait
Jun 28, 2026
Merged

feat(traits): add ControlDependence family + refactor strict-contract helpers#471
ocots merged 2 commits into
mainfrom
feat/control-dependence-trait

Conversation

@ocots

@ocots ocots commented Jun 28, 2026

Copy link
Copy Markdown
Member

Summary

This PR adds a new trait family ControlDependence to CTBase.Traits and refactors the internal trait-contract machinery to eliminate duplication.

What changed

  1. New ControlDependence family — a strict opt-in trait encoding whether an optimal control problem carries a control input:

    • Tags: ControlFree (no control: ẋ = f(t,x,v)) and WithControl (with control: ẋ = f(t,x,u,v))
    • Contract: types implement has_control_dependence_trait and control_dependence accessors
    • Derived predicates: is_control_free and has_control
    • 25 unit tests mirroring the variable-dependence test structure
    • Ready for Holy-trait dispatch in CTFlows to close a latent bug in Flow(ocp) dispatch
  2. Refactored strict-contract helpers — eliminated 70% duplication across time_dependence, variable_dependence, mutability, and now control_dependence:

    • New internal helpers _throw_missing_trait and _throw_trait_not_implemented factor the fallback exception bodies
    • Generalised _caller_function_name stacktrace filter: now matches any has_<family>_trait name via pattern matching instead of a hard-coded blacklist
    • All three families now call the shared helpers; error messages and behaviour are byte-identical to before
  3. Minor cleanups:

    • Collapsed has_variable duplicate to direct alias: has_variable(obj) = is_variable(obj)
    • Added control_dependence.jl to the auto-generated API page in docs/api_reference.jl
  4. Documentation:

    • Module docstring now documents the two trait templates: strict opt-in (no safe default → loud failure) vs default-valued capability (safe conservative default)
    • Added Control dependence section to guide/traits.md with examples
    • Added rationale note on abstract-vs-concrete trait types: abstract traits (TimeDependence) are used only as type-parameter values; concrete singletons work everywhere

Why this matters

Control-dependence will unblock PR2 (CTModels) and PR3 (CTFlows) in the ecosystem-wide traits cleanup:

  • CTModels will declare the trait on Model, reading from the type of ocp.control (EmptyControlModel → ControlFree), making is_control_free type-stable instead of runtime.
  • CTFlows will dispatch Flow(ocp) on the trait, properly routing to different code paths and closing a latent correctness bug where a with-control OCP would silently mis-build a Hamiltonian.

Verification

  • Traits test suite: 301/301 ✓ (including 25 new tests for ControlDependence and updated export-list checks)
  • Docs build: clean (VitePress link check passes)
  • Caller-name detection: verified via REPL that is_control_free(3.14) names the predicate correctly through the new shared-helper chain

Design philosophy

The ControlDependence family follows the exact pattern of VariableDependence — a verbatim calque with proven precedent. No macro abstraction yet; the shared helper refactor is sufficient at 4 families. If a 5th strict family appears, that's the trigger to extract a @trait_contract macro to eliminate remaining boilerplate.


🤖 Generated with Claude Code

- New strict opt-in family with tags ControlFree and WithControl for encoding control
  presence in OCPs at the type level, ready for dispatch in downstream packages
- Factor shared contract helpers _throw_missing_trait and _throw_trait_not_implemented to
  eliminate 70% duplication across time_dependence, variable_dependence, and mutability
  families; behaviour and messages unchanged
- Generalise _caller_function_name stacktrace filter to match any has_<family>_trait
  predicate (startswith + endswith pattern) instead of hard-coded names
- Collapse has_variable duplicate to direct alias is_variable(obj)
- Add 25 unit tests mirroring variable_dependence test structure; Traits suite: 301/301 ✓
- Document the two trait templates (strict opt-in vs default-valued capability) in
  module docstring; add control-dependence section to guide/traits.md with rationale
  for abstract-vs-concrete trait types (abstract used for type-param-only axes)
- Update api_reference.jl to include control_dependence.jl in the auto-generated API page

Follows the exact pattern of variable_dependence for consistency. No changes to
existing predicates; _caller_function_name detection now names is_control_free through
the new shared helper chain.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@ocots
ocots merged commit 9df51b3 into main Jun 28, 2026
4 checks passed
@ocots
ocots deleted the feat/control-dependence-trait branch June 28, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant