Version history

What's new in v3.10

  • A new MulStyle trait called TwoArg has been added. It should be used for LinearMaps that do not admit a mutating multiplication à la (3-arg or 5-arg) mul!, but only out-of-place multiplication à la A * x. Products (aka CompositeMaps) and sums (aka LinearCombinations) of TwoArg-LinearMaps now have memory-optimized multiplication kernels. For instance, A*B*C*x for three TwoArg-LinearMaps A, B and C now allocates only y = C*x, z = B*y and the result of A*z.
  • The construction of function-based LinearMaps, typed FunctionMap, has been rearranged. Additionally to the convenience constructor LinearMap{T=Float64}(f, [fc,] M, N=M; kwargs...), the newly exported constructor FunctionMap{T,iip}(f, [fc], M, N; kwargs...) is readily available. Here, iip is either true or false, and encodes whether f (and fc if present) are mutating functions. In the convenience constructor, this is determined via the Bool keyword argument ismutating and may not be fully inferred.

What's new in v3.9

  • The application of LinearMaps to vectors operation, i.e., (A,x) -> A*x = A(x), is now differentiable w.r.t. to the input x for integration with machine learning frameworks such as Flux.jl. The reverse differentiation rule makes A::LinearMap usable as a static, i.e., non-trainable, layer in a network, and requires the adjoint A' of A to be defined.
  • New map types called KhatriRaoMap and FaceSplittingMap are introduced. These correspond to lazy representations of the column-wise Kronecker product and the row-wise Kronecker product (or "transposed Khatri-Rao product"), respectively. They can be constructed from two matrices A and B via khatrirao(A, B) and facesplitting(A, B), respectively. The first is particularly efficient as it makes use of the vec-trick for Kronecker products and computes y = khatrirao(A, B) * x for a vector x as y = vec(B * Diagonal(x) * transpose(A)). As such, the Khatri-Rao product can actually be built for general LinearMaps, including function-based types. Even for moderate sizes of 5 or more columns, this map-vector product is faster than first creating the explicit Khatri-Rao product in memory and then multiplying with the vector; not to mention the memory savings. Unfortunately, similar efficiency cannot be achieved for the face-splitting product.

What's new in v3.8

  • A new map called InverseMap is introduced. Letting an InverseMap act on a vector is equivalent to solving the linear system, i.e. InverseMap(A) * b is the same as A \ b. The default solver is ldiv!, but can be specified with the solver keyword argument to the constructor (see the docstring for details). Note that A must be compatible with the solver: A can, for example, be a factorization, or another LinearMap in combination with an iterative solver.
  • New constructors for lazy representations of Kronecker products (squarekron) and sums (sumkronsum) for square factors and summands, respectively, are introduced. They target cases with 3 or more factors/summands, and benchmarking intended use cases for comparison with KroneckerMap (constructed via Base.kron) and KroneckerSumMap (constructed via kronsum) is recommended.

What's new in v3.7

  • mul!(M::AbstractMatrix, A::LinearMap, s::Number, a, b) methods are provided, mimicking similar methods in Base.LinearAlgebra. This version allows for the memory efficient implementation of in-place addition and conversion of a LinearMap to Matrix. Efficient specialisations for WrappedMap, ScaledMap, and LinearCombination are provided. If users supply the corresponding _unsafe_mul! method for their custom maps, conversion, construction, and inplace addition will benefit from this supplied efficient implementation. If no specialisation is supplied, a generic fallback is used that is based on feeding the canonical basis of unit vectors to the linear map.

  • A new map type called EmbeddedMap is introduced. It is a wrapper of a "small" LinearMap (or a suitably converted AbstractVecOrMat) embedded into a "larger" zero map. Hence, the "small" map acts only on a subset of the coordinates and maps to another subset of the coordinates of the "large" map. The large map L can therefore be imagined as the composition of a sampling/projection map P, of the small map A, and of an embedding map E: L = E ⋅ A ⋅ P. It is implemented, however, by acting on a view of the vector x and storing the result into a view of the result vector y. Such maps can be constructed by the new methods:

    • LinearMap(A::MapOrVecOrMat, dims::Dims{2}, index::NTuple{2, AbstractVector{Int}}), where dims is the dimensions of the "large" map and index is a tuple of the x- and y-indices that interact with A, respectively;
    • LinearMap(A::MapOrVecOrMat, dims::Dims{2}; offset::Dims{2}), where the keyword argument offset determines the dimension of a virtual upper-left zero block, to which A gets (virtually) diagonally appended.
  • An often requested new feature has been added: slicing (i.e., non-scalar indexing) any LinearMap object via Base.getindex overloads. Note, however, that only rather efficient complete slicing operations are implemented: A[:,j], A[:,J], and A[:,:], where j::Integer and J is either of type AbstractVector{<:Integer>} or an AbstractVector{Bool} of appropriate length ("logical slicing"). Partial slicing operations such as A[I,j] and A[I,J] where I is as J above are disallowed.

    Scalar indexing A[i::Integer,j::Integer] as well as other indexing operations that fall back on scalar indexing such as logical indexing by some AbstractMatrix{Bool}, or indexing by vectors of (linear or Cartesian) indices are not supported; as an exception, getindex calls on wrapped AbstractVecOrMats is forwarded to corresponding getindex methods from Base and therefore allow any type of usual indexing/slicing. If scalar indexing is really required, consider using A[:,j][i] which is as efficient as a reasonable generic implementation for LinearMaps can be.

    Furthermore, (predominantly) horizontal slicing operations require the adjoint operation of the LinearMap type to be defined, or will fail otherwise. Important note: LinearMap objects are meant to model objects that act on vectors efficiently, and are in general not backed up by storage-like types like Arrays. Therefore, slicing of LinearMaps is potentially slow, and it may require the (repeated) allocation of standard unit vectors. As a consequence, generic algorithms relying heavily on indexing and/or slicing are likely to run much slower than expected for AbstractArrays. To avoid repeated indexing operations which may involve redundant computations, it is strongly recommended to consider converting LinearMap-typed objects to Matrix or SparseMatrixCSC first, if memory permits.

What's new in v3.6

  • Support for Julia versions below v1.6 has been dropped.
  • Block[Diagonal]Map, CompositeMap, KroneckerMap and LinearCombination type objects can now be backed by a Vector of LinearMap-type elements. This can be beneficial in cases where these higher-order LinearMaps are constructed from many maps where a tuple backend may get inefficient or impose hard work for the compiler at construction. The default behavior, however, does not change, and construction of vector-based LinearMaps requires usage of the unexported constructors ("expert usage"), except for constructions like sum([A, B, C]) or prod([A, B, C]) (== C*B*A), where A, B and C are of some LinearMap type.

What's new in v3.5

  • WrappedMap, ScaledMap, LinearCombination, AdjointMap, TransposeMap and CompositeMap, instead of using the default axes(A) = map(oneto, size(A)), now forward calls to axes to the underlying wrapped linear map. This allows allocating operations such as * to determine the appropriate storage and axes type of their outputs. For example, linear maps that wrap BlockArrays will, upon multiplicative action, produce a BlockArrays.PseudoBlockVector with block structure inherited from the operator's output axes axes(A,1).

What's new in v3.4

  • In WrappedMap constructors, as implicitly called in addition and mutliplication of LinearMaps and AbstractMatrix objects, (conjugate) symmetry and positive definiteness are only determined for matrix types for which these checks are expected to be very cheap or even known at compile time based on the concrete type. The default for LinearMap subtypes is to call, for instance, issymmetric, because symmetry properties are either stored or easily obtained from constituting maps. For custom matrix types, define corresponding methods LinearMaps._issymmetric, LinearMaps._ishermitian and LinearMaps._isposdef to hook into the property checking mechanism.

What's new in v3.3

  • AbstractVectors can now be wrapped by a LinearMap just like AbstractMatrixtyped objects. Upon wrapping, they are not implicitly reshaped to matrices. This feature might be helpful, for instance, in the lazy representation of rank-1 operatorskron(LinearMap(u), v') == ⊗(u, v') == u ⊗ v'for vectorsuandv. The action on vectors,(u⊗v')x, is implemented optimally viau(v'x)`.

What's new in v3.2

  • In-place left-multiplication mul!(Y, X, A::LinearMap) is now allowed for X::AbstractMatrix and implemented via the adjoint equation Y' = A'X'.

What's new in v3.1

  • In Julia v1.3 and above, LinearMap-typed objects are callable on AbstractVectors: For L::LinearMap and x::AbstractVector, L(x) = L*x.

What's new in v3.0

  • BREAKING change: Internally, any dependence on former A*_mul_B! methods is abandonned. For custom LinearMap subtypes, there are now two options:
    1. In case your type is invariant under adjoint/transposition (i.e., adjoint(L::MyLinearMap)::MyLinearMap similar to, for instance, LinearCombinations or CompositeMaps), At_mul_B! and Ac_mul_B! do not require any replacement! Rather, multiplication by L' is, in this case, handled by mul!(y, L::MyLinearMap, x[, α, β]).
    2. Otherwise, you will need to define mul! methods with the signature mul!(y, L::TransposeMap{<:Any,MyLinearMap}, x[, α, β]) and mul!(y, L::AdjointMap{<:Any,MyLinearMap}, x[, α, β]).
  • Left multiplying by a transpose or adjoint vector (e.g., y'*A) produces a transpose or adjoint vector output, rather than a composite LinearMap.
  • Block concatenation now handles matrices and vectors directly by internal promotion to LinearMaps. For [h/v/hc]cat it suffices to have a LinearMap object anywhere in the list of arguments. For the block-diagonal concatenation via SparseArrays.blockdiag, a LinearMap object has to appear among the first 8 arguments. This restriction, however, does not apply to block-diagonal concatenation via; dims=(1,2)).
  • Introduction of more expressive and visually appealing show methods, replacing the fallback to the generic show.

What's new in v2.7

  • Potential reduction of memory allocations in multiplication of LinearCombinations, BlockMaps, and real- or complex-scaled LinearMaps. For the latter, a new internal type ScaledMap has been introduced.
  • Multiplication code for CompositeMaps has been refactored to facilitate to provide memory for storage of intermediate results by directly calling helper functions.

What's new in v2.6

  • New feature: "lazy" Kronecker product, Kronecker sums, and powers thereof for LinearMaps. AbstractMatrix objects are promoted to LinearMaps if one of the first 8 Kronecker factors is a LinearMap object.
  • Compatibility with the generic multiply-and-add interface (a.k.a. 5-arg mul!) introduced in julia v1.3

What's new in v2.5

  • New feature: concatenation of LinearMaps objects with UniformScalings, consistent with (h-, v-, and hc-)concatenation of matrices. Note, matrices A must be wrapped as LinearMap(A), UniformScalings are promoted to LinearMaps automatically.

What's new in v2.4

  • Support restricted to Julia v1.0+.

What's new in v2.3

  • Fully Julia v0.7/v1.0/v1.1 compatible.
  • Full support of noncommutative number types such as quaternions.

What's new in v2.2

  • Fully Julia v0.7/v1.0 compatible.
  • A convert(SparseMatrixCSC, A::LinearMap) function, that calls the sparse matrix generating function.

What's new in v2.1

  • Fully Julia v0.7 compatible; dropped compatibility for previous versions of Julia from LinearMaps.jl v2.0.0 on.
  • A 5-argument version for mul!(y, A::LinearMap, x, α=1, β=0), which computes y := α * A * x + β * y and implements the usual 3-argument mul!(y, A, x) for the default α and β.
  • Synonymous convert(Matrix, A::LinearMap) and convert(Array, A::LinearMap) functions, that call the Matrix constructor and return the matrix representation of A.
  • Multiplication with matrices, interpreted as a block row vector of vectors:
    • mul!(Y::AbstractArray, A::LinearMap, X::AbstractArray, α=1, β=0): applies A to each column of X and stores the result in-place in the corresponding column of Y;
    • for the out-of-place multiplication, the approach is to compute convert(Matrix, A * X); this is equivalent to applying A to each column of X. In generic code which handles both A::AbstractMatrix and A::LinearMap, the additional call to convert is a noop when A is a matrix.
  • Full compatibility with Arpack.jl's eigs and svds; previously only eigs was working. For more, nicely collaborating packages see the Example section.