Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
392701a
first pass
icweaver May 31, 2025
2feea5c
added some usage docs
icweaver Jun 3, 2025
b84257e
added ref link between Home and Examples pages
icweaver Jul 12, 2025
2c27270
updated QuantityArray docstring to include convenience method
icweaver Jul 12, 2025
f1b4706
remove accidentally added file
icweaver Jul 14, 2025
5a59406
removed ref link
icweaver Jul 14, 2025
2e72c26
Merge branch 'main' into quantityarray-construction
icweaver Jul 14, 2025
f08023b
Update README.md
icweaver Jul 14, 2025
c15675f
Update README.md
icweaver Jul 14, 2025
12b20d2
switched to relative header link
icweaver Jul 15, 2025
096ca12
fix remaining ambiguities
icweaver Jul 29, 2025
52a54fd
relax Dates compat
icweaver Jul 29, 2025
8b00bb0
remove accidentally added .gitignore file
icweaver Jul 29, 2025
1e6b410
put back original .gitignore
icweaver Jul 29, 2025
0c8b97a
revert doc build-specific stuff from Abhro's PR
icweaver Aug 2, 2025
b27ec64
remove Dates.jl-specific stuff
icweaver Aug 2, 2025
4c88d04
Merge branch 'main' into quantityarray-construction
icweaver Aug 3, 2025
87e8b1f
Merge branch 'JuliaPhysics:main' into quantityarray-construction
icweaver Aug 3, 2025
046b8d4
use tighter subtyping to avoid method ambiguity with Dates.jl
icweaver Aug 5, 2025
0d3fcd8
reorg + some tests
icweaver Aug 6, 2025
3b185a9
Merge branch 'main' into quantityarray-construction
icweaver Aug 6, 2025
227ece9
reorg
icweaver Aug 6, 2025
51270a2
Merge branch 'main' into quantityarray-construction
icweaver Aug 12, 2025
768972b
Merge branch 'main' into quantityarray-construction
icweaver Aug 22, 2025
cdfce08
updated examples
icweaver Aug 29, 2025
42d62b4
cleanup
icweaver Aug 30, 2025
e110662
more LA tests
icweaver Aug 30, 2025
8f34f3b
put back accidentally deleted coverage.jl
icweaver Aug 30, 2025
7869b73
extra test highlighted by codecov
icweaver Sep 1, 2025
3c97dab
simplify usage examples
icweaver Sep 1, 2025
017d5d4
simplified type checks
icweaver Sep 1, 2025
97ec277
Merge branch 'main' into quantityarray-construction
icweaver Sep 11, 2025
7ae45ef
tweak README example
MilesCranmer Sep 11, 2025
c475043
unused variable
MilesCranmer Sep 11, 2025
7dab92a
reduce docstring
MilesCranmer Sep 11, 2025
01b9e5e
use nicer syntax elsewhere
MilesCranmer Sep 11, 2025
22767a5
fix: nested QuantityArray issue
MilesCranmer Sep 11, 2025
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,22 @@ julia> @btime $f.(qa) setup=(xa = randn(100000) .* u"km/s"; qa = QuantityArray(x

So we can see the `QuantityArray` version saves on both time and memory.

By default, DynamicQuantities will create a `QuantityArray` from an `AbstractArray`, similarly to how a `Quantity` is created from a scalar in the [Usage](@ref) examples:

```julia
using DynamicQuantities # hide

x = [0.3, 0.4, 0.5]u"km/s"

y = (42:45) * u"kg"
```

This can be overridden to produce a vector of `Quantity`s by explicitly broadcasting the unit:

```julia
z = [0.3, 0.4, 0.5] .* u"km/s"
```

### Unitful

DynamicQuantities allows you to convert back and forth from Unitful.jl:
Expand Down
3 changes: 3 additions & 0 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ the same dimension) by passing an array and a single quantity:

```julia
x = QuantityArray(randn(32), u"km/s")
# or x = randn(32)u"km/s"
```

or, by passing an array of individual quantities:
Expand Down Expand Up @@ -281,6 +282,8 @@ f_square(v) = v^2 * 1.5 - v^2
println("Applying function to y_q: ", sum(f_square.(y_q)))
```

See [Home > Arrays](https://ai.damtp.cam.ac.uk/dynamicquantities/stable/#arrays) for more.

### Fill

We can also make `QuantityArray` using `fill`:
Expand Down
8 changes: 8 additions & 0 deletions src/arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ and so can be used in most places where a normal array would be used, including

# Constructors

The most convenient way to create a `QuantityArray` is by multiplying your array-like object by the desired dimension(s), e.g.,

```julia
x = [3, 4, 5]u"km/s"
```

For more control, the following constructors are available:

- `QuantityArray(v::AbstractArray, d::AbstractDimensions)`: Create a `QuantityArray` with value `v` and dimensions `d`,
using `Quantity` if the eltype of `v` is numeric, and `GenericQuantity` otherwise.
- `QuantityArray(v::AbstractArray{<:Number}, q::AbstractQuantity)`: Create a `QuantityArray` with value `v` and dimensions inferred
Expand Down
5 changes: 5 additions & 0 deletions src/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ for (type, base_type, _) in ABSTRACT_QUANTITY_TYPES
function Base.:/(l::AbstractDimensions, r::$type)
new_quantity(typeof(r), inv(ustrip(r)), l / dimension(r))
end

# Defining here instead of outside loop with UnionAbstractQuantity to avoid method ambiguities
Base.:*(A::AbstractArray, q::$type) = QuantityArray(A, q)
Base.:*(q::$type, A::AbstractArray) = A * q
Base.:/(A::AbstractArray, q::$type) = A * inv(q)
end
end

Expand Down
23 changes: 13 additions & 10 deletions test/unittests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ end
end

@testset "Arrays" begin
T_QA_GenericQuantity(T, N) = QuantityArray{T, N, D, Q, V} where {T, D<:Dimensions, Q<:UnionAbstractQuantity, V<:AbstractArray{T, N}}
for T in [Float16, Float32, Float64], R in [Rational{Int16}, Rational{Int32}, SimpleRatio{Int}, SimpleRatio{SafeInt16}]
D = Dimensions{R}

Expand All @@ -288,9 +289,9 @@ end
@test ustrip(x + ones(T, 32))[32] == 2
@test typeof(x + ones(T, 32)) <: GenericQuantity{Vector{T}}
@test typeof(x - ones(T, 32)) <: GenericQuantity{Vector{T}}
@test typeof(ones(T, 32) * GenericQuantity(T(1), D, length=1)) <: GenericQuantity{Vector{T}}
@test typeof(ones(T, 32) / GenericQuantity(T(1), D, length=1)) <: GenericQuantity{Vector{T}}
@test ones(T, 32) / GenericQuantity(T(1), length=1) == GenericQuantity(ones(T, 32), length=-1)
@test typeof(ones(T, 32) * GenericQuantity(T(1), D, length=1)) <: T_QA_GenericQuantity(T, 1)
@test typeof(ones(T, 32) / GenericQuantity(T(1), D, length=1)) <: T_QA_GenericQuantity(T, 1)
@test ones(T, 32) / GenericQuantity(T(1), length=1) == QuantityArray(ones(T, 32), GenericQuantity(T(1), length=-1))
end

@testset "isapprox" begin
Expand Down Expand Up @@ -395,17 +396,19 @@ end
end

@testset "Multiplying ranges with units" begin
T_QA_StepRangeLen = QuantityArray{T, 1, D, Q, V} where {T, D<:Dimensions, Q<:UnionAbstractQuantity, V<:StepRangeLen}
T_QA_s_StepRangeLen = QuantityArray{T, 1, D, Q, V} where {T, D<:SymbolicDimensions, Q<:UnionAbstractQuantity, V<:StepRangeLen}
# Test multiplying ranges with units
x = (1:0.25:4)u"inch"
@test x isa StepRangeLen
@test x isa T_QA_StepRangeLen
Copy link
Member

@MilesCranmer MilesCranmer Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@test x isa T_QA_StepRangeLen
@test x isa QuantityArray
@test DQ.array_type(x) <: StepRangeLen
@test DQ.dim_type(x) <: Dimensions

bit more readable like this.

Same comment for other T_QA_AbstractArray types - we can swap them for using the interface to inspect the type parameters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will swap these out. Sorry, by using the interface to inspect type parameters, is there something else that you would like to see instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@test first(x) == 1u"inch"
@test x[2] == 1.25u"inch"
@test last(x) == 4u"inch"
@test length(x) == 13

# Integer range (but real-valued unit)
x = (1:4)u"inch"
@test x isa StepRangeLen
@test x isa T_QA_StepRangeLen
@test first(x) == 1u"inch"
@test x[2] == 2u"inch"
@test last(x) == 4u"inch"
Expand All @@ -414,7 +417,7 @@ end

# Test with floating point range
x = (1.0:0.5:3.0)u"m"
@test x isa StepRangeLen
@test x isa T_QA_StepRangeLen
@test first(x) == 1.0u"m"
@test x[2] == 1.5u"m"
@test last(x) == 3.0u"m"
Expand All @@ -427,30 +430,30 @@ end

# Test with symbolic units
x = (1:0.25:4)us"inch"
@test x isa StepRangeLen{<:Quantity{Float64,<:SymbolicDimensions}}
@test x isa T_QA_s_StepRangeLen
@test first(x) == us"inch"
@test x[2] == 1.25us"inch"
@test last(x) == 4us"inch"
@test length(x) == 13

# Test that symbolic units preserve their symbolic nature
x = (0:0.1:1)us"km/h"
@test x isa AbstractRange
@test x isa QuantityArray
@test first(x) == 0us"km/h"
@test x[2] == 0.1us"km/h"
@test last(x) == 1us"km/h"
@test length(x) == 11

# Similarly, integers should stay integers:
x = (1:4)us"inch"
@test_skip x isa StepRangeLen{<:Quantity{Int64,<:SymbolicDimensions}}
@test_skip x isa T_QA_s_StepRangeLen
@test first(x) == us"inch"
@test x[2] == 2us"inch"
@test last(x) == 4us"inch"
@test length(x) == 4

# With RealQuantity:
@test_skip (1.0:4.0) * RealQuantity(u"inch") isa StepRangeLen{<:RealQuantity{Float64,<:SymbolicDimensions}}
@test_skip (1.0:4.0) * RealQuantity(u"inch") isa T_QA_s_StepRangeLen
# TODO: This is not available as TwicePrecision interacts with Real in a way
# that demands many other functions to be defined.
end
Expand Down