API Reference
This page documents the public API of HeterogeneousArrays.
Types
HeterogeneousArrays.AbstractHeterogeneousVector — Type
AbstractHeterogeneousVector{T, S} <: AbstractVector{T}The abstract base type for all heterogeneous vectors.
HeterogeneousArrays.HeterogeneousVector — Type
HeterogeneousVector{T, S} <: AbstractVector{T}A segmented vector that stores mixed types while maintaining type-stable broadcasting.
Fields can contain scalars (wrapped for mutability), arrays, or quantities with units. The flattened view presents all elements as a single AbstractVector for broadcasting.
Constructors
HeterogeneousVector(; kwargs...)- Create with named fieldsHeterogeneousVector(args...)- Create with positional args (auto-named field1, field2, ...)HeterogeneousVector(x::NamedTuple)- Create from a NamedTuple
Arguments
x::NamedTuple: Internal storage (users shouldn't access directly)
Examples
Named fields:
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(x = 1.0, y = 2.5, z = [1, 2, 3]);
julia> v.x
1.0
julia> v.z
3-element Vector{Int64}:
1
2
3Indexing
Base.getindex — Method
Base.getindex(hv::AbstractHeterogeneousVector, idx::Int)Index into the flattened view of the HeterogeneousVector.
The vector presents a flat 1-based indexed interface where indices are mapped sequentially across all fields in order. Scalar fields count as a single element, and array fields contribute their length to the total.
Arguments
hv: The HeterogeneousVector to indexidx::Int: The 1-based index into the flattened view
Returns
The element at the given flattened index
Errors
- Throws
BoundsErrorifidxis outside the range[1, length(hv)]
Performance Warning
This method is not type-stable. The return type depends on which field contains the requested index, and the compiler cannot determine this at compile time. This forces the return type to be a union of all possible field element types, preventing optimization.
For performance-critical code, use named field access instead of integer indexing:
v[1]— Not type-stable (avoid in loops)v.field[1]— Type-stable (preferred for performance)
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2, 3], b = 4.5);
julia> v[1] # First element of field 'a'
1
julia> v[4] # Field 'b' (scalar)
4.5
julia> v[5] # Out of bounds
ERROR: BoundsErrorBase.setindex! — Method
Base.setindex!(hv::AbstractHeterogeneousVector, val, idx::Int)Assign a value at the flattened index in a HeterogeneousVector.
Index mapping follows the same flattened convention as getindex. For scalar fields, the new value replaces the wrapped value. For array fields, the element is updated in place.
Arguments
hv: The HeterogeneousVector to modifyval: The new value to assignidx::Int: The 1-based index into the flattened view
Returns
The value that was assigned
Errors
- Throws
BoundsErrorifidxis outside the range[1, length(hv)]
Performance Warning
Like getindex, this method is not type-stable and should be avoided in performance-critical code. Use named field assignment instead:
v[1] = x— Not type-stable (avoid in loops)v.field[1] = x— Type-stable (preferred for performance)
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2, 3], b = 4.5);
julia> v[2] = 99
99
julia> v.a
3-element Vector{Int64}:
1
99
3
julia> v[4] = 10.0
10.0
julia> v.b
10.0Base.length — Method
Base.length(hv::AbstractHeterogeneousVector) -> IntReturn the total length of the HeterogeneousVector as the sum of all field lengths.
Scalar fields (wrapped in Ref) contribute 1 to the total, and array fields contribute their full length. This is the length of the flattened view used for indexing.
Returns
The total number of elements in the flattened representation
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2, 3], b = 4.5, c = [10, 20]);
julia> length(v) # 3 (from 'a') + 1 (from 'b') + 2 (from 'c')
6Base.size — Method
Base.size(hv::AbstractHeterogeneousVector) -> TupleReturn the size of the HeterogeneousVector as a 1-tuple of its total length.
This satisfies the AbstractArray interface by returning (length(hv),), representing a 1-dimensional array.
Returns
A tuple (n,) where n = length(hv)
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(x = [1, 2], y = 3.0);
julia> size(v)
(3,)Base.iterate — Method
Base.iterate(hv::AbstractHeterogeneousVector) -> Union{Tuple, Nothing}
Base.iterate(hv::AbstractHeterogeneousVector, state) -> Union{Tuple, Nothing}Iterate over all elements in the HeterogeneousVector using the flattened view.
The vector is traversed field-by-field in the order they are stored in the internal NamedTuple. Scalar fields yield a single value, and array fields yield each of their elements in sequence.
Returns
- On first call:
(element, state)ornothingif the vector is empty - On subsequent calls with state: next
(element, state)ornothingwhen exhausted
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> for (i, element) in enumerate(v)
println(i, ": ", element)
end
1: 1
2: 2
3: 3.0Property Access
Base.getproperty — Method
Base.getproperty(hv::HeterogeneousVector, name::Symbol)Access a named field in the HeterogeneousVector.
Arguments
hv::HeterogeneousVector: The vectorname::Symbol: Field name
Returns
The value of the field (unwrapped if it's a scalar)
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(x = 1.0, y = 2.0);
julia> v.x
1.0See Also
Base.setproperty!(::HeterogeneousVector, ::Symbol, ::Any)propertynames
Base.setproperty! — Method
Base.setproperty!(hv::HeterogeneousVector, name::Symbol, value)Set a named field in the HeterogeneousVector.
Arguments
hv::HeterogeneousVector: The vectorname::Symbol: Field namevalue: New value for the field
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(x = 1.0, y = 2.0);
julia> v.x = 5.0;
julia> v.x
5.0See Also
Base.getproperty(::HeterogeneousVector, ::Symbol)propertynames
Allocation & Copying
Base.copy — Method
Base.copy(bc::Broadcast.Broadcasted{Broadcast.Style{AbstractHeterogeneousVector}}) -> HeterogeneousVectorMaterialize a broadcast operation into a new HeterogeneousVector.
When a broadcast expression involves a HeterogeneousVector, this method is called to allocate and fill the result. The operation is performed field-by-field, allowing type-stable operations on each segment independently.
Arguments
bc: A broadcasted expression tree rooted at a HeterogeneousVector
Returns
A new HeterogeneousVector containing the broadcasted results, with all fields computed
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> result = copy(Broadcast.broadcasted(+, v, 2));
julia> result.a
2-element Vector{Int64}:
3
4
julia> result.b
5.0Base.copyto! — Function
Base.copyto!(dst::AbstractHeterogeneousVector, src::AbstractHeterogeneousVector) -> AbstractHeterogeneousVectorCopy data from a source HeterogeneousVector into a destination HeterogeneousVector.
Both vectors must have the same field names. Data is copied in-place into the destination's existing storage, preserving its array references for array fields and updating scalar wrappings.
Arguments
dst: The destination vector (will be modified)src: The source vector (unchanged)
Returns
The modified dst vector
Errors
- Throws an
errorifdstandsrchave different field names
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> w = zero(v); # Create an empty vector with same structure
julia> copyto!(w, v); # Copy data from v into w
julia> w.a
2-element Vector{Int64}:
1
2Base.copyto!(dest::AbstractHeterogeneousVector, bc::Broadcast.Broadcasted) -> AbstractHeterogeneousVectorMaterialize a broadcast operation in-place into a HeterogeneousVector.
The broadcast result is computed field-by-field and stored directly into the destination vector's existing storage. For array fields, this uses Broadcast.materialize!() for in-place operations. For scalar fields, the result is assigned to the wrapped value.
Arguments
dest: The destination HeterogeneousVector (will be modified)bc: A broadcasted expression tree
Returns
The modified dest vector
Errors
- Throws
ArgumentErrorif the broadcast expression involves a HeterogeneousVector with different field names thandest
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1.0, 2.0], b = 3.0);
julia> w = HeterogeneousVector(a = [10.0, 20.0], b = 5.0);
julia> copyto!(v, Broadcast.broadcasted(+, v, w));
julia> v.a
2-element Vector{Float64}:
11.0
22.0
julia> v.b
8.0Base.copyto!(dest::AbstractArray, bc::Broadcast.Broadcasted{Broadcast.Style{AbstractHeterogeneousVector{Names}}})Materialize a HeterogeneousVector broadcast result into a flat AbstractArray.
This is the "bridge" between structured heterogeneous data and standard numerical solvers. It allows computing residuals or norms from mixed-unit data and storing them in a plain, contiguous float array.
Storage Layout
The result is flattened field-by-field according to the order in Names. For a vector with fields pos (length 2) and time (length 1):
dest[1:2]contains results fromposdest[3]contains results fromtime
Examples
julia> using HeterogeneousArrays, Unitful
julia> v = HeterogeneousVector(pos = [1.0u"m", 2.0u"m"], time = 10.0u"s");
julia> v_proj = HeterogeneousVector(pos = [1.5u"m", 3.0u"m"], time = 5.0u"s");
julia> # The ODE solver provides a plain Vector{Float64}
julia> residuals = Vector{Float64}(undef, length(v));
julia> # This triggers the flattening copyto!
julia> residuals .= ustrip.(v .- v_proj);
julia> residuals
3-element Vector{Float64}:
-0.5
-1.0
5.0Base.similar — Method
Base.similar(hv::HeterogeneousVector) -> HeterogeneousVectorCreate a new HeterogeneousVector with the same structure and field names, with uninitialized storage.
Each field is initialized by calling similar() on its type, creating uninitialized (or zeroed) storage of the same shape and element type as the original. Use zero() if you want zeroed values.
Arguments
hv: The template HeterogeneousVector
Returns
A new HeterogeneousVector with the same field names and types, containing uninitialized data
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> s = similar(v);
julia> length(s.a)
2Base.similar(hv::HeterogeneousVector, ::Type{T}, ::Type{S}, R::DataType...)Construct a new HeterogeneousVector with the same structure and field names as hv, but with potentially different element types for each individual field.
This variadic method allows for "per-field" type specialization, similar to ArrayPartition in RecursiveArrayTools. It is particularly useful when you need to maintain heterogeneity (e.g., keeping one field as an Int while converting another to a Float64).
Arguments
hv: The templateHeterogeneousVector.T,S,R...: A sequence of types. The total number of types provided must exactly match the number of fields inhv.
Returns
- A
HeterogeneousVectorwhere the i-th field has the i-th provided type. Note that memory is uninitialized (viasimilar).
Errors
- Throws a
DimensionMismatchif the number of types provided does not match the number of fields in the vector.
Implementation Note
This function uses ntuple with a compile-time length to ensure the resulting NamedTuple is type-inferred correctly by the Julia compiler.
Base.similar(hv::HeterogeneousVector, ::Type{ElType})Construct a new HeterogeneousVector with the same structure and field names as hv, but with all fields converted to the same uniform element type ElType.
This method satisfies the standard AbstractArray interface. It is essential for ensuring that broadcasting operations (e.g., hv .* 1.0) return a HeterogeneousVector rather than collapsing into a standard flat Array.
Arguments
hv: The templateHeterogeneousVector.ElType: The target type for all segments/fields within the new vector.
Returns
- A
HeterogeneousVectorwhere every field's elements are of typeElType.
Implementation Note
Uses map over the field names to recursively call _similar_field on each segment, preserving the original NamedTuple keys.
similar(hv::HeterogeneousVector; kwargs...)Construct a new HeterogeneousVector with the same field names and structure as hv, optionally overriding the element types of specific fields.
This method allows for high-level, name-based type transformation. If a field name is provided as a keyword argument, the new vector will use the specified type for that segment. Fields not mentioned in kwargs will preserve their original element types.
Arguments
hv::HeterogeneousVector: The template vector providing the names and structure.kwargs...: Pairs offieldname = Typeused to redefine specific segments.
Returns
- A
HeterogeneousVectorwith uninitialized (or zeroed) data in the requested types.
Errors
- Throws an
ArgumentErrorif any key inkwargsdoes not match an existing field name inhv. This prevents silent failures caused by typos in field names.
Performance Note
This implementation avoids Dict allocations by operating directly on the kwargs NamedTuple, making it more efficient and "compiler-friendly" than dictionary-based lookups.
Example
julia> using HeterogeneousArrays, Unitful
julia> v = HeterogeneousVector(pos = [1.0, 2.0]u"m", id = [10, 20]);
julia> # Change 'id' to Float64 and 'pos' to a different unit/type
v2 = similar(v, id = Float64, pos = Float32);
julia> eltype(v2.id)
Float64
julia> # Typos in field names will now trigger an error
similar(v, poss = Float64)
ERROR: ArgumentError: Field 'poss' does not exist in HeterogeneousVector. Available fields: (:pos, :id)Base.zero — Method
Base.zero(hv::HeterogeneousVector) -> HeterogeneousVectorCreate a new HeterogeneousVector filled with zero values matching the structure of hv.
Each field is zeroed according to its element type. Scalar fields receive zero(T), and array fields receive zero(array) (an all-zeros array of the same shape).
Arguments
hv: The template HeterogeneousVector
Returns
A new HeterogeneousVector with the same field names and types, filled with zeros
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> z = zero(v);
julia> z.a
2-element Vector{Int64}:
0
0
julia> z.b
0.0Broadcasting
Base.Broadcast.BroadcastStyle — Method
Base.BroadcastStyle(::Type{<:AbstractHeterogeneousVector}) -> BroadcastStyleDefine the broadcast style for HeterogeneousVector to enable type-stable broadcasting.
The HeterogeneousVector uses a custom broadcast style to ensure that broadcasting operations preserve the heterogeneous structure and field names. When multiple HeterogeneousVectors are involved in a broadcast operation, they must have compatible field names.
Broadcast Rules
Single HeterogeneousVector with other types: The broadcast result preserves the HeterogeneousVector structure and field names.
Multiple HeterogeneousVectors with matching field names: All vectors must have identical field names; operations proceed field-by-field in parallel.
Multiple HeterogeneousVectors with different field names: Throws an error to prevent silent data corruption.
Examples
julia> using HeterogeneousArrays
julia> v = HeterogeneousVector(a = [1, 2], b = 3.0);
julia> w = HeterogeneousVector(a = [10, 20], b = 5.0);
julia> result = v .+ w; # Element-wise addition preserves structure
julia> result.a
2-element Vector{Int64}:
11
22