GPU-safe constructors, custom metric signatures, and Julia 1.10 LTS#37
Open
AndersEdin wants to merge 5 commits into
Open
GPU-safe constructors, custom metric signatures, and Julia 1.10 LTS#37AndersEdin wants to merge 5 commits into
AndersEdin wants to merge 5 commits into
Conversation
Make arrays of Clifford numbers usable on the GPU.
- Constructors: replace interpolated `@assert` messages in
`check_element_count`, the `KVector` K-range check, and the
`Z2CliffordNumber` parity check with constant `String` literals. An
interpolated message allocates a `String` in the throw path (via
`print_to_string`/`apply_generic`), which GPU compilers reject
(`jl_alloc_string` is unsupported on-device). The checks are kept; for
concrete types they are statically true and fully eliminated, so the
constructors emit zero throws and zero allocations.
- Adapt: add `ext/CliffordNumbersAdaptExt.jl` registering
`Adapt.adapt_structure`. These types are `isbits` with `NTuple{L,T}`
storage, so the only meaningful adaptation is retyping the scalar
coefficients to match a typed-array destination (e.g.
`adapt(Array{Float32}, ::KVector{...,Float64})` -> `...,Float32`); other
destinations preserve the scalar type. Wired into `[weakdeps]`/
`[extensions]`/`[compat]` and covered by `test/ext/Adapt.jl`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On-device testing (RTX 3070, CUDA.jl) showed that `reverse`, `adjoint`, `conj`, and `grade_involution` did not lower on the GPU for `CliffordNumber` (dense) and `Z2CliffordNumber`, and `reverse` failed for `KVector` too: the generic fallback `T(x[f.(BladeIndices(T))])` resolves blade positions at runtime through `to_index`, which the GPU compiler rejects (gpu_gc_pool_alloc + dynamic dispatch). This blocked the headline `rotate.(cu_pts, Ref(R))` / rotor-sandwich path, since the sandwich uses `R'`. - Replace the generic indexing loop with a `@generated` `_sign_automorphism` that bakes every blade position and sign at compile time, emitting pure tuple arithmetic (allocation-free, GPU-safe). Works for all dense / Z2 / user types. - Add the closed-form `reverse(::KVector)` (identical to `adjoint` for real scalars), the one case the existing KVector closed forms had missed. Verified: the four automorphisms and a rotor-sandwich broadcast now compile and run on-device and match the CPU result. A new CPU regression testset pins all four automorphisms against the original indexing semantics across dense / Even / Odd / KVector over VGA(3), PGA(3), and STA. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
metric_tuple(s) returns a signature's metric as an NTuple of Int8 and is the single, world-age-robust source of metric data: the generated blade kernels and the negative/zero square-bit masks both read it, and it works on a downstream AbstractSignature subtype by reading its interface at the caller's world age rather than inside a generator. This makes user-defined signatures a supported extension path. A custom metric is expressed either as a Signature *value* placed in the Clifford number's type parameter, or as an AbstractSignature subtype bridged to a value with Signature(s) (mirroring how CliffordNumbers' own STAP is built). docs/src/custom_signatures.md walks through both, and test/custom_signature.jl exercises a phase-space (D, D, R) family: the signature interface, metric_tuple as the single metric source, the world-age limitation of a bare subtype, and the Signature(s) bridge driving the generated product/automorphism/inverse kernels end to end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raise the compat lower bound to 1.10 in the package and test environments (1.9 support was already nominal: package extensions are the only 1.9 feature in use). dimension of a plain Signature now returns a full-width Int instead of an Int8: on 1.10, binomial with a narrow integer falls back to the Float64 gamma-function method, which made nblades of a KVector in a custom-signature algebra return a Float64 and broke construction. The zero-allocation test gates assert what the current stable compiler constant-folds; the 1.10 compiler misses some of those folds, so ALLOCATION_GATES restricts them to 1.12 and later. Correctness and inference tests are unchanged and run on every version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Here comes the bug fixes and maintenance fixes from my previous PR. The issues were found when implementing a geometric algebra package using CliffordNumbers, and both the GPU fixes and the fix to enable defining metric signatures are needed for being able to use the package.
I have raised the minimum Julia to the 1.10 LTS, which makes sense since it is a lot of work to support older versions. This also aligns with what is already written in the package documentation.
Changes
Julia 1.10 LTS. Compat lower bound raised to
1.10in the package and test environments.dimension(::Signature)now returns a full-widthIntinstead ofInt8: on 1.10 a narrow integer sendsbinomialto itsFloat64gamma-function method, which madenbladesof aKVectorin a custom-signature algebra return aFloat64and broke construction. Zero-allocation test gates are placed behindALLOCATION_GATES = VERSION >= v"1.12"; the 1.10 compiler misses some of the constant-folds those gates assert, so they are informative-only there while correctness and inference tests run on every version.GPU enablement. Adds an
Adaptpackage extension soAbstractCliffordNumbervalues adapt to/from device arrays, plus constructor paths that allocate no boxed intermediates on the device path. The sign-changing automorphisms (reverse,grade_involution,conj) are reimplemented as branch-free bit arithmetic on the blade grade — reversion(-1)^{k(k-1)/2}, grade involution(-1)^k— so they carry no data-dependent branch and codegen uniformly on the GPU. Results are unchanged.Custom metric signatures.
metric_tuple(s) -> NTuple{N,Int8}is the single, world-age-robust source of metric data; the generated kernels and thenegative/zerosquare-bit masks all read it. A user metric is expressed either as aSignaturevalue in the type parameter, or as anAbstractSignaturesubtype reduced to a value withSignature(s)(the built-inSTAPuses the same shape). A bare downstream subtype cannot drive the@generatedkernels — their generators run at CliffordNumbers' load-time world age and cannot see interface methods defined later — soSignature(s), computed at the caller's world age, is the bridge. Tutorial indocs/src/custom_signatures.md.Tests
test/custom_signature.jlexercises a phase-space(D, D, R)family: signature interface,nbladesintegrality on 1.10,metric_tupleas the sole metric source, the world-age failure of a bare subtype, and theSignature(s)bridge driving the generated product/automorphism/inverse kernels. GPU extension covered bytest/ext/Adapt.jl.Pkg.test()passes on the 1.10 LTS.Breaking