Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ A Julia package for symbolic manipulation of second-quantized operators used in
src/SecondQuantizedAlgebra.jl # Main module, exports, imports
src/types.jl # QField, QSym abstract hierarchy
src/precompile.jl # PrecompileTools workload
src/average.jl # AvgFunc, average(), undo_average(), Hermitian-Real symtype
src/numeric.jl # to_numeric(), numeric_average() via QuantumOpticsBase
src/average.jl # AvgFunc/AvgSym, average(), undo_average(), Hermitian-Real symtype

src/numeric/backend.jl # NumericBackend + singletons; open hooks; _default_backend
src/numeric/coeff.jl # _to_complex family, constant folder, parameter/time splitting
src/numeric/core.jl # NumericContext, _numeric_leaf, _to_numeric_static/_td
src/numeric/indexed.jl # backend-neutral indexed unroll (sites, ne, sub_op/sub_coef)
src/numeric/api.jl # public to_numeric / numeric_average / expect (QI types)

ext/SecondQuantizedAlgebraQuantumOpticsBaseExt.jl # QuantumOpticsBase backend (vector LazySum)
ext/SecondQuantizedAlgebraQuantumToolboxExt.jl # QuantumToolbox backend (VecSum over QobjEvo)

src/expressions/cnum.jl # CNum = Complex{Num} arithmetic, fast paths, constants
src/expressions/qterm.jl # QTerm struct (ops, ne) — dict key for QAdd
Expand Down Expand Up @@ -72,7 +80,7 @@ HilbertSpace (abstract)
- **Dict-based term storage**: `QAdd` stores `Dict{QTerm, CNum}` where `QTerm` bundles `ops::Vector{QSym}` with `ne::Vector{NonEqualPair}` index-inequality scope, plus a cached `hash::UInt` (computed once at construction; the key is hashed repeatedly per dict insert/probe/rehash). Like terms are collected on construction.
- **CNum prefactors**: prefactors are `Complex{Num}` (from Symbolics.jl), not parameterized. Dedicated fast paths in `cnum.jl` short-circuit for numeric (non-symbolic) cases.
- **Site-indexed operators**: each `Op` carries `space_index` and `index::Index`. Operators interact only if `_same_site(a, b)`.
- **Five operator hooks**: `_site_compare`, `_can_commute`, `_commute_pair`, `_reduce_pair`, `_ground_state_expand` are each a single `(::Op, ::Op)` method (in `operators.jl`) that branches on `kind`. The algebra talks to operators exclusively through these. A sixth, defaulted hook `_may_reduce(a, b)::Bool` gates the reduce pass (`true` only for `Transition`/`Pauli` pairs). Adding a new operator role means adding an `OP_*` enum arm, a constructor, an `is_*` predicate, and a `kind` branch in each hook plus `adjoint`/`order_key`/`to_numeric`/printing. With the concrete `Op` eltype the hooks now infer concrete return types (`_commute_pair`/`_reduce_pair` return `Tuple{Op, …}`), so `_may_reduce`'s original boxing-avoidance role is moot; it remains as a cheap same-site skip.
- **Five operator hooks**: `_site_compare`, `_can_commute`, `_commute_pair`, `_reduce_pair`, `_ground_state_expand` are each a single `(::Op, ::Op)` method (in `operators.jl`) that branches on `kind`. The algebra talks to operators exclusively through these. A sixth, defaulted hook `_may_reduce(a, b)::Bool` gates the reduce pass (`true` only for `Transition`/`Pauli` pairs). Adding a new operator role means adding an `OP_*` enum arm, a constructor, an `is_*` predicate, and a `kind` branch in each hook plus `adjoint`/`order_key`/`numeric_operator` (per backend extension)/printing. With the concrete `Op` eltype the hooks now infer concrete return types (`_commute_pair`/`_reduce_pair` return `Tuple{Op, …}`), so `_may_reduce`'s original boxing-avoidance role is moot; it remains as a cheap same-site skip.
- **Concrete struct fields**: all struct fields are concretely typed (enforced by CheckConcreteStructs in tests).
- **Index tracking**: `QAdd` carries `indices::Vector{Index}` for summation scope; per-term inequality constraints live on `QTerm.ne`, not on `QAdd`.

Expand Down Expand Up @@ -160,11 +168,13 @@ Before merging any PR:
| Package | Purpose |
|---------|---------|
| Combinatorics | Product enumeration for operator generation |
| QuantumOpticsBase | Numeric basis types (FockBasis, NLevelBasis, SpinBasis), `⊗`, LazyTensor |
| QuantumInterface | Lightweight owner of `⊗`/`tensor`/`expect`/`basis` and `Basis`/`AbstractOperator`/`StateVector` types (hard dep) |
| SymbolicUtils | Symbolic tree traversal interface |
| Symbolics | Symbolic variables (`@variables`), `Num` type for CNum prefactors |
| TermInterface | `iscall`, `operation`, `arguments` protocol |
| Latexify | LaTeX rendering recipes |
| PrecompileTools | `@setup_workload`/`@compile_workload` in `precompile.jl` |

**Test dependencies:** Aqua, JET, CheckConcreteStructs, ExplicitImports, LaTeXStrings
**Weak dependencies (numeric extensions):** `QuantumOpticsBase` (FockBasis/NLevelBasis/SpinBasis, vector `LazySum`/`TimeDependentSum`), `QuantumToolbox` + `SciMLOperators` (QuantumObject builders, the `VecSum` over `QobjEvo`). The numeric API errors until one backend is loaded.

**Test dependencies:** Aqua, JET, CheckConcreteStructs, ExplicitImports, LaTeXStrings, QuantumOpticsBase, QuantumToolbox, SciMLOperators, LinearAlgebra
19 changes: 18 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@ All notable changes to [`SecondQuantizedAlgebra.jl`](https://github.com/qojulia/

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v0.10.0]

Numeric conversion (`to_numeric`/`numeric_average`/`expect`) was redesigned to be extensible, type-stable, and multi-backend. This is a breaking release.

## [v0.10.0]
### Added

- A second numeric backend, [QuantumToolbox.jl](https://github.com/qutip/QuantumToolbox.jl), alongside QuantumOpticsBase.jl. Both are wired in through Julia package extensions and selected with the backend singletons `QuantumOpticsBackend()` / `QuantumToolboxBackend()`.
- A uniform Hilbert-space entry point `to_numeric(op, h::HilbertSpace, dims; backend, parameter, time_parameter, operators, adjoint_ops, op_type)`. It builds the backend basis from `dims` (Fock cutoff / spin number) and is the only form that works for both backends. The backend defaults to the single loaded one.
- Open extension hooks `numeric_operator`, `numeric_basis`, `numeric_subbasis`, `numeric_embed`, `numeric_identity`, `numeric_assemble`, `numeric_assemble_td`, `numeric_materialize`, `numeric_expect` are exported. Downstream packages add methods to support custom operator roles or backends without editing this package.

### Changed (breaking)

- `QuantumOpticsBase` moved from a hard dependency to a weak dependency. Using the numeric API now requires loading a backend: add `using QuantumOpticsBase` (or `using QuantumToolbox`) next to `using SecondQuantizedAlgebra`. The lightweight `QuantumInterface.jl` is a new hard dependency (it owns the `⊗`/`tensor`/`expect`/`basis` generics that the algebra extends).
- The time-dependent form (`time_parameter` non-empty) returns the backend's **native** time-dependent operator: a `TimeDependentSum` (QuantumOptics) or a `QobjEvo` (QuantumToolbox), both directly consumable by `mesolve`/`master_dynamic`/`sesolve`. It is no longer a `t -> op(t)` closure.

### Changed

- Averages of provably Hermitian operators (`adjoint(A) == A`) now carry `symtype === Real` instead of `Number`. This gives a faster `simplify` path and makes `conj(⟨a'a⟩)` fold to `⟨a'a⟩` rather than an inert `conj(...)` wrapper; indexed sums and lifted time-dependent variables inherit the typing, which survives `substitute`. Resolves [#171](https://github.com/qojulia/SecondQuantizedAlgebra.jl/issues/171).

### Notes

- The `op_type` contract from v0.9.2 is unchanged and is now backend-neutral: `to_numeric` assembles the operator lazily and materializes it once via `op_type` (default `sparse`), so the return type depends only on `op_type`, not on the expression shape. `op_type=identity` opts into the natural lazy representation (`LazyTensor`/`LazyProduct`/`LazySum` / `VecSum`), `op_type=dense` into a dense operator. The lazy form is the internal assembly primitive and is what `numeric_average`/`expect` consume directly, so `LazyKet` states work without materializing.
- A user doing `using SecondQuantizedAlgebra, QuantumToolbox` has two `⊗`/`tensor`/`expect` in scope (QuantumInterface's and QuantumToolbox's) and must qualify them; importing QuantumToolbox as `import QuantumToolbox as QTB` avoids the clash.


## [v0.9.4]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ setup:
${JULIA} -e 'using Pkg; Pkg.Apps.add("Runic")'

format: ## Format all Julia files with Runic
runic --inplace src/ test/ benchmark/ examples/ docs/
runic --inplace src/ ext/ test/ benchmark/ examples/ docs/

servedocs:
${JULIA} --project=docs -e 'using LiveServer; LiveServer.servedocs(skip_files=[joinpath("docs", "src", "changelog.md")])'
Expand Down
20 changes: 16 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,35 @@ version = "0.10.0"

[deps]
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e"
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678"
QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5"
SciMLPublic = "431bcebd-1456-4ced-9d72-93c2757fff0b"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c"

[weakdeps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678"
QuantumToolbox = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab"
SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961"

[extensions]
SecondQuantizedAlgebraQuantumOpticsBaseExt = "QuantumOpticsBase"
SecondQuantizedAlgebraQuantumToolboxExt = ["QuantumToolbox", "SciMLOperators", "LinearAlgebra"]

[compat]
Combinatorics = "1.0.2"
FunctionWrappers = "1"
Latexify = "0.13, 0.14, 0.15, 0.16"
LinearAlgebra = "1"
MutableArithmetics = "1"
PrecompileTools = "1"
QuantumOpticsBase = "0.4, 0.5"
QuantumInterface = "0.4"
QuantumOpticsBase = "0.5"
QuantumToolbox = "0.47"
SciMLOperators = "1.22"
SciMLPublic = "1"
SymbolicUtils = "4.30"
Symbolics = "7.26"
Expand Down
16 changes: 16 additions & 0 deletions docs/src/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,22 @@ to_numeric
numeric_average
```

### Numeric backends

```@docs
QuantumOpticsBackend
QuantumToolboxBackend
numeric_operator
numeric_basis
numeric_subbasis
numeric_embed
numeric_identity
numeric_assemble
numeric_assemble_td
numeric_materialize
numeric_expect
```


## [Symbolic Summations](@id API: Sums)

Expand Down
Loading
Loading