Skip to content

Commit 0caa995

Browse files
committed
✨ feat: new vectors & operators code
Signed-off-by: nstarman <nstarman@users.noreply.github.com>
1 parent bcd3cec commit 0caa995

File tree

204 files changed

+11804
-22111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

204 files changed

+11804
-22111
lines changed

.github/copilot-instructions.md

Lines changed: 160 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ This is a UV workspace repository containing multiple packages:
1616

1717
- **Language**: Python 3.11+
1818
- **Main API**: Vector types, coordinate transformations, and reference frames
19-
- Vector types: `CartesianPos3D`, `SphericalPos`, `CylindricalPos`, etc.
20-
- Angle and Distance types with units via `unxt`
21-
- `vconvert()`: Transform between coordinate representations
19+
- `coordinax.angle.Angle` and `coordinax.distance.Distance` types with units
20+
via `unxt`
21+
- Vector types `coordinax.Vector` with representations in `coordinax.r`.
22+
- `vconvert()`: Transform between vector representations
2223
- Operators on vectors: `GalileanRotateOp`, `GalileanBoostOp`, etc.
2324
- Frame and their transformations: `frames.frame_transform_op()`
2425
- Coordinates with frames: `Coordinate`.
@@ -30,9 +31,7 @@ This is a UV workspace repository containing multiple packages:
3031
## Architecture & Core Components
3132

3233
- **Vector types** (hierarchical):
33-
- `AbstractPos`: Base class for position vectors
34-
- `AbstractVel`: Base class for velocity vectors
35-
- `AbstractAcc`: Base class for acceleration vectors
34+
- `AbstractRep`: Base class for coordinate vectors
3635
- Concrete implementations: Cartesian, Spherical, Cylindrical, etc.
3736
- All vectors are `ArrayValue` subclasses (Quax protocol) for JAX integration
3837
- **Angle and Distance types**: Specialized scalar types with units
@@ -68,9 +67,9 @@ This is a UV workspace repository containing multiple packages:
6867
### Main Package Structure (`/src/coordinax/`)
6968

7069
- `_src/`: Private implementation code
71-
- `vectors/`: Vector classes (position, velocity, acceleration)
7270
- `angles.py`: Angle type implementation
7371
- `distances/`: Distance type implementations
72+
- `vectors/`: Vector classes (position, velocity, acceleration)
7473
- `frames/`: Reference frame definitions and transformations
7574
- `operators/`: Frame-aware operators
7675
- `_coordinax_space_frames/`: Frame-specific coordinate spaces
@@ -81,6 +80,8 @@ This is a UV workspace repository containing multiple packages:
8180

8281
- Always use type hints (standard typing, `jaxtyping.Array`, `ArrayLike`, shape
8382
annotations)
83+
- **NEVER use `from __future__ import annotations`** - causes issues with plum
84+
dispatch and runtime type introspection
8485
- Extensive use of Plum multiple dispatch - check `.methods` on any function to
8586
see all dispatches
8687
- Runtime type checking via `beartype` for validation
@@ -93,6 +94,151 @@ This is a UV workspace repository containing multiple packages:
9394
- Prefer `u.Q` over `u.Quantity` for creating quantities (shorter and more
9495
concise)
9596

97+
### Multiple Dispatch with Plum
98+
99+
This project heavily relies on `plum-dispatch` for multiple dispatch, which
100+
allows different implementations of the same function based on argument types.
101+
Understanding how plum works is critical for working with this codebase.
102+
103+
#### Multiple Dispatch Mechanism
104+
105+
- **Single-dispatch vs Multiple-dispatch**: Unlike single dispatch (e.g.,
106+
`functools.singledispatch`), plum selects implementations based on ALL
107+
argument types, not just the first one
108+
- **Type-based routing**: Plum examines the runtime types of all arguments and
109+
selects the most specific matching implementation
110+
- **Dispatch decorator**: Use `@dispatch` to register multiple implementations
111+
of the same function name
112+
113+
Example:
114+
115+
```python
116+
from plum import dispatch
117+
118+
119+
@dispatch
120+
def process(x: int) -> str:
121+
return f"integer: {x}"
122+
123+
124+
@dispatch
125+
def process(x: float) -> str:
126+
return f"float: {x}"
127+
128+
129+
@dispatch
130+
def process(x: int, y: int) -> str:
131+
return f"two integers: {x}, {y}"
132+
```
133+
134+
#### Finding All Dispatches
135+
136+
**CRITICAL**: When working with dispatched functions, you MUST check all
137+
registered implementations. A function may have dozens of overloads.
138+
139+
**Two methods to find all dispatches:**
140+
141+
1. **Use `.methods` attribute** (preferred in Python REPL/notebooks):
142+
143+
```python
144+
from coordinax import vconvert
145+
146+
print(vconvert.methods) # Shows all registered dispatch signatures
147+
```
148+
149+
2. **Search the codebase** (preferred when coding):
150+
- Search for `@dispatch` followed by the function name
151+
- Look for all `def function_name(...)` definitions with `@dispatch`
152+
- Example: searching for `@dispatch\ndef vconvert` finds all vconvert
153+
overloads
154+
155+
**Why this matters:**
156+
157+
- You might find a more specific dispatch that handles your exact case
158+
- Prevents accidentally adding duplicate dispatches
159+
- Reveals the complete API surface and supported type combinations
160+
- Essential for understanding how different vector types interact
161+
162+
#### Parametric Classes
163+
164+
Plum's `@parametric` decorator enables type parametrization, creating distinct
165+
types for different parameters:
166+
167+
```python
168+
from plum import parametric
169+
170+
171+
@parametric
172+
class Container(type_parameter):
173+
def __init__(self, value):
174+
self.value = value
175+
176+
177+
# Creates distinct types:
178+
IntContainer = Container[int]
179+
FloatContainer = Container[float]
180+
```
181+
182+
**In this codebase:**
183+
184+
- Vector types can be parametric (though less common than in `unxt`)
185+
- Enables type-aware multiple dispatch for vector transformations
186+
- Example: Different dispatch paths based on vector representation type
187+
188+
**Key properties:**
189+
190+
- Parametric types are cached (same parameters = same type object)
191+
- Type parameters can be strings, tuples, or other hashable objects
192+
- Use `get_type_parameter(obj)` to retrieve the parameter from an instance
193+
- Parametric classes enable representation checking at dispatch time
194+
195+
#### Type Promotion with `plum.promote`
196+
197+
`plum.promote` implements automatic type promotion for mixed-type operations:
198+
199+
```python
200+
from plum import dispatch, promote
201+
202+
203+
@dispatch
204+
def add(x: int, y: int) -> int:
205+
return x + y
206+
207+
208+
@dispatch
209+
def add(x: float, y: float) -> float:
210+
return x + y
211+
212+
213+
# Without promotion:
214+
add(1, 2.5) # Error: no dispatch for (int, float)
215+
216+
217+
# With promotion (defined separately):
218+
@dispatch
219+
def add(x: promote(int, float), y: float) -> float:
220+
return add(float(x), y)
221+
```
222+
223+
**In this codebase:**
224+
225+
- `plum.promote` is used to convert between vector types and representations
226+
- Common pattern: promote scalars to distance/angle types
227+
- Enables natural operations like `vector + CartesianPos3D(...)`
228+
229+
**Usage pattern:**
230+
231+
1. Define core implementations for specific types
232+
2. Add promotion dispatches to handle mixed types
233+
3. Promotion dispatches typically convert arguments and redispatch
234+
235+
**Important notes:**
236+
237+
- Promotion order matters: `promote(int, float)` != `promote(float, int)`
238+
- Keep promotion logic explicit and minimal
239+
- Prefer concrete dispatches over heavy promotion use
240+
- Document promotion behavior when it's non-obvious
241+
96242
### JAX Integration via Quax
97243

98244
- Vectors are `ArrayValue` subclasses (Quax protocol)
@@ -127,11 +273,18 @@ This is a UV workspace repository containing multiple packages:
127273
- `nox -s docs`: build documentation (add `--serve` to preview)
128274
- `nox -s pytest_benchmark`: run CodSpeed benchmarks
129275

276+
**IMPORTANT**: Never write temporary files outside the repository (e.g., to
277+
`/tmp/` or other system directories). Always use paths within the repository for
278+
any file operations, including temporary or scratch files.
279+
130280
## Testing
131281

132282
- Use `pytest` for all test suites with Sybil for doctests in code and markdown
133283
- Add unit tests for every new function or class
134284
- Test organization: `unit/`, `integration/`, `benchmark/`
285+
- **All tests must actually test something**: Every test function must include
286+
`assert` statements or return values that pytest can validate. Empty test
287+
bodies or tests that only call functions without verification are not valid.
135288
- Optional dependencies handled via
136289
`optional_dependencies.OptionalDependencyEnum`
137290
- Tests requiring optional deps auto-skip if not installed

0 commit comments

Comments
 (0)