Interface
VectorInterface.jl exports a small, fixed set of methods that together constitute the vector-space interface. This page walks through each method group, with examples.
For every mutating operation, three variants are provided:
f(args...)— pure, allocating, always returns a new object.f!(args...)— strict in-place, errors if the in-place update is not possible (e.g. the target is immutable or the scalar types are incompatible).f!!(args...)— tries in-place, falls back to allocating when needed. This follows the convention ofBangBang.jland is the right choice when writing generic algorithms that should be both efficient on mutable inputs and correct on immutable ones.
Scalar type
VectorInterface.scalartype — Function
scalartype(x)Returns the type of scalar over which the vector-like object x behaves as a vector, e.g. the type of scalars with which x could be scaled in-place. This function should also work in the type domain, i.e. if x is a vector like object, then also scalartype(typeof(x)) should work.
scalartype returns the type of scalars over which the vector-like object behaves as a vector. For an AbstractArray{T <: Number} this is T, and for a nested array Vector{Vector{Float64}} it is still Float64 (whereas eltype would return Vector{Float64}).
julia> scalartype([1.0, 2.0, 3.0])
Float64
julia> scalartype(Vector{Vector{Float64}})
Float64Custom types should implement the type-domain method scalartype(::Type{MyType}). The value-domain method scalartype(x) is defined generically as scalartype(typeof(x)).
Zero vector
VectorInterface.zerovector — Function
zerovector(x, [S::Type{<:Number} = scalartype(x)])Returns a zero vector in the vector space of x. Optionally, a modified scalar type S for the resulting zero vector can be specified.
Also see: zerovector!, zerovector!!
VectorInterface.zerovector! — Function
VectorInterface.zerovector!! — Function
zerovector!!(x, [S::Type{<:Number} = scalartype(x)])Construct a zero vector in the vector space of x, thereby trying to overwrite and thus recycle x when possible. Optionally, a modified scalar type S for the resulting zero vector can be specified.
New types should only implement the one-argument version. The two-argument version amounts to S == scalartype(x) ? zerovector!!(x) : zerovector(x, S)
Also see: zerovector, zerovector!
zerovector(v) returns a new zero vector of the same type as v. An optional second argument S <: Number overrides the scalar type of the result:
julia> zerovector([1.0, 2.0, 3.0])
3-element Vector{Float64}:
0.0
0.0
0.0
julia> zerovector([1.0, 2.0, 3.0], ComplexF64)
3-element Vector{ComplexF64}:
0.0 + 0.0im
0.0 + 0.0im
0.0 + 0.0imzerovector!(v) zeros v in place, and zerovector!!(v) does the same when possible and otherwise allocates. For nested vectors, the in-place variants recurse, so zerovector!(v) on a Vector{Vector{Float64}} does not allocate any new inner arrays.
Scaling
VectorInterface.scale — Function
scale(x, α::Number)Computes the new vector-like object obtained from scaling x with the scalar α.
VectorInterface.scale! — Function
scale!(x, α::Number) -> x
scale!(y, x, α::Number) -> yRescale x with the scalar coefficient α, thereby overwrite and thus recylcing the contents of x (in the first form) or y (in the second form). This is only possible if x, respectively y is mutable, and if the scalar types involed are compatible and can be promoted and converted.
VectorInterface.scale!! — Function
scale!!(x, α::Number)
scale!!(y, x, α::Number)Rescale x with the scalar coefficient α, thereby trying to overwrite and thus recylce the contents of x (in the first form) or y (in the second form). A new object will be created when this fails due to immutability, incompatible scalar types or incommensurate sizes.
scale(v, α) rescales the vector v by the scalar α and returns a new object. scale!(v, α) and scale!(w, v, α) perform the rescaling in place (into v or into w, respectively), and require the scalar types to be compatible. scale!! tries to do the operation in place and falls back to allocating otherwise.
julia> v = [1.0, 2.0, 3.0];
julia> scale(v, 2.0)
3-element Vector{Float64}:
2.0
4.0
6.0
julia> scale!(v, 0.5); v
3-element Vector{Float64}:
0.5
1.0
1.5A key design point: scale! on a nested vector recurses into the inner vectors and operates fully in place, in contrast to LinearAlgebra.rmul! which allocates new inner arrays. For v = [[1.0, 2.0], [3.0, 4.0]], scale!(v, 0.5) does not allocate.
Linear combinations
VectorInterface.add — Function
add(y, x, [α::Number = 1, β::Number = 1])Add y and x, or more generally construct the linear combination y * β + x * α.
VectorInterface.add! — Function
add!(y, x, [α::Number = One(), β::Number = One()])Add y and x, or more generally construct the linear combination y * β + x * α, storing the result in y. This will error in case of incompatible scalar types or incommensurate sizes.
VectorInterface.add!! — Function
add!!(y, x, [α::Number = 1, β::Number = 1])Add y and x, or more generally construct the linear combination y * β + x * α, thereby trying to store the result in y. A new object will be created when this fails due to immutability, incompatible scalar types or incommensurate sizes.
add(y, x, α, β) computes y * β + x * α and returns a new object. The in-place variants add!/add!! overwrite y with the result. The default coefficients are One(), so the short forms add(y, x) and add(y, x, α) compute y + x and y * 1 + x * α respectively.
julia> add([1.0, 2.0], [10.0, 20.0])
2-element Vector{Float64}:
11.0
22.0
julia> add([1.0, 2.0], [10.0, 20.0], 0.1)
2-element Vector{Float64}:
2.0
4.0
julia> add([1.0, 2.0], [10.0, 20.0], 0.5, 2.0)
2-element Vector{Float64}:
7.0
14.0Custom types should implement only the four-argument form. When desired, the One()-coefficient case can be specialized for efficiency by dispatching on α::One and/or β::One.
Inner product and norm
VectorInterface.inner — Function
inner(x, y)Compute the inner product between x and y.
inner(x, y) is similar to LinearAlgebra.dot but is stricter about the arguments it accepts: both x and y must be elements of the same vector space, with compatible scalar types and shapes. The norm is re-exported directly from LinearAlgebra:
julia> inner([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
32.0
julia> norm([3.0, 4.0])
5.0For complex vectors, inner(x, y) is conjugate-linear in its first argument, matching the mathematical convention.
Hard-coded coefficients: One and Zero
VectorInterface.One — Type
struct One endSingleton type for representing a hard-coded constant 1 in vector addition / linear combinations.
VectorInterface.Zero — Type
struct Zero endSingleton type for representing a hard-coded constant 0 in vector addition / linear combinations.
Now I am become Zero, the destroyer of NaN. - VishnuOne and Zero are singleton subtypes of Number used to represent hard-coded constant coefficients in linear combinations. They allow methods like add to dispatch on a unit coefficient at compile time, avoiding unnecessary multiplications. They are the default values for the α and β coefficients in add, add!, and add!!.
Supported types
Out of the box, VectorInterface.jl provides implementations for:
<:Number— scalars are also vectors over themselves.<:AbstractArray— bothAbstractArray{<:Number}and nested arrays, with recursive in-place behaviour for the latter.TupleandNamedTuplewith a homogeneous element scalar type, again with arbitrary nesting. For example,Vector{NTuple{3, Matrix{Float64}}}is supported.
A general fallback exists for types that already implement LinearAlgebra.rmul!, axpy!, axpby!, mul!, and LinearAlgebra.dot, but this fallback emits a warning and is intended only as a transitional measure that may be removed in a future release. New types should implement the VectorInterface.jl methods directly.
Implementing the interface for a new type
A new vector-like type T should implement, at a minimum:
scalartype(::Type{T})zerovector(x::T, ::Type{S})whereS <: Numberscale(x::T, α::Number)add(y::T, x::T, α::Number, β::Number)inner(x::T, y::T)LinearAlgebra.norm(x::T)
If the type is mutable, it should additionally implement the !-variants (zerovector!, scale!, add!). The !!-variants then have generic fallbacks: they delegate to the !-variant when the type is mutable and to the non-mutating variant otherwise.
The MinimalVec type included in the package's source (src/minimalvec.jl) is a worked example: it wraps an AbstractVector and implements exactly the VectorInterface.jl API, deliberately excluding all other AbstractArray methods. It is useful both as a reference and as a test harness for checking that an algorithm depends only on the minimal interface.