Linear and multilinear functions
Linear extensions
LinearCombinations.@linear
— Macro@linear f
This macro defines a linear extension of the function (or callable object) f
. More specifically, it defines a new method f(a::AbstractLinear{T,R}; kw...) where {T,R}
that returns the linear combination obtained by summing up c*f(x)
for all term-coefficient pairs x => c
appearing in a
.
The new method recognizes the following keyword arguments:
coefftype
: This optional keyword argument specifies the coefficient type of the linear combination returned byf(a)
if the keyword argumentaddto
is not present. Ifcoefftype
is also not specified andf(x::T)
is a term (as opposed to a linear combination), thencoefftype
is set toR
. Iff(x::T) <: AbstractLinear
, say with coefficient typeS
, thenpromote_type(R, S)
is chosen as the new coefficient type. If theaddto
keyword is present, thencoefftype
is ignored.Because of the way Julia handles keyword arguments, the form
f(a; coefftype = Int)
is not type-stable. Type stability can be achieved by sayingf(a; coefftype = Val(Int))
.addto::AbstractLinear
: If given, the sum of all termsc*f(x)
is added toaddto
, and the result is returned. This avoids allocating a new linear combination each timef
is called with anAbstractLinear
argument. The default value foraddto
isLinear{U,coefftype}
. HereU
is the return type off(x::T)
if this return type is not a subtype ofAbstractLinear
and the term type of the return values otherwise.coeff
: This optional keyword argument allows to efficiently compute scalar multiples off(a)
. More precisely,f(a; coeff = c)
returnsc*f(a)
, andf(a; addto = b, coeff = c)
addsc*f(a)
tob
and returns this new value.sizehint::Bool = true
: The new method forf
may callsizehint!
foraddto
to pre-allocate room for the new terms. This keyword argument permits to turn pre-allocation off.
All other keyword arguments are passed on to f(x)
. With the macro @linear_kw
one can make f(a)
pass the special keyword arguments listed above on to f(x)
, too.
See also @multilinear
, sizehint!
, @linear_kw
, keeps_filtered
.
Examples
Linear extension of a function returning a term
julia> f(x) = uppercase(x); @linear f
f (generic function with 2 methods)
julia> a = Linear('x' => 1, 'y' => 2)
x+2*y
julia> f(a)
2*Y+X
julia> f(a; coefftype = Float64)
2.0*Y+X
julia> b = Linear('z' => 3); f(a; addto = b, coeff = -1); b
-2*Y-X+3*z
Linear extension of a function returning a linear combination
julia> g(x) = Linear(x*x => 1.0, string(x) => -1.0); @linear g
g (generic function with 2 methods)
julia> g("x"), g("")
(xx-x, 0)
julia> g(a) # same a as before
xx-x+2.0*yy-2.0*y
julia> g(a; coefftype = Val(Int), coeff = 3.0)
3*xx-3*x+6*yy-6*y
Linear extension of a callable object
julia> struct P y::String end
julia> (p::P)(x) = p.y*x*p.y; @linear p::P
julia> p = P("w"); p(a) # same a as before
wxw+2*wyw
LinearCombinations.LinearExtension
— TypeLinearExtension{F}
This type is the linear extension of the given type F
.
Examples
julia> const g = LinearExtension(uppercase)
LinearExtension(uppercase)
julia> g('x')
'X': ASCII/Unicode U+0058 (category Lu: Letter, uppercase)
julia> a = Linear('x' => 1, 'y' => 2); g(a; coeff = 3)
6*Y+3*X
LinearCombinations.matrixrepr
— Functionmatrixrepr(f, b1::AbstractBasis, b0::AbstractBasis, ::Type{R})) where R
Return a matrix representing the linear map f
with respect to the bases b0
(source) and b1
(target). Coefficients have the type R
.
See also matrixrepr!
.
Example
julia> @linear f; f(x) = Linear(uppercase(x) => 1, 'A' => 1)
f (generic function with 2 methods)
julia> matrixrepr(f, Basis('A':'C'), Basis('a':'c'), Int)
3×3 Matrix{Int64}:
2 1 1
0 1 0
0 0 1
LinearCombinations.matrixrepr!
— Functionmatrixrepr!(f, a::AbstractMatrix, b1::AbstractBasis, b0::AbstractBasis; iszero::Bool = false) -> a
Compute a matrix representing the linear map f
with with respect to the bases b0
(source) and b1
(target). Store the result in a
and return a
. If the keyword argument iszero
is true
, then the matrix a
is not first initialized with zeros.
See also matrixrepr
.
LinearCombinations.diff
— Functiondiff(x)
The differential of x
.
The module LinearCombinations only defines the linear extension of diff
, but no methods for terms. The only exception is diff
for tensors.
See also diff(t::AbstractTensor)
.
LinearCombinations.coprod
— Functioncoprod(x)
The coproduct (or comultiplication) of x
, which is assumed to be an element of a coalgebra.
The module LinearCombinations only defines the linear extension of coprod
, but no methods for terms, except for tensors.
See also coprod(t::AbstractTensor)
.
Multilinear extensions
LinearCombinations.@multilinear
— Macro@multilinear f
@multilinear f f0
This macro defines a multilinear extension of the function f
(or f0
). This is analogous to @linear f
. The new methods accepts both terms and linear combinations as arguments. It linearly expands all arguments that are linear combinations and then calls f
for each combination of terms. If f0
is specified, then f0
is called instead to evaluate terms.
The new method always returns a linear combination (of type Linear
unless this is overriden by the addto
keyword). The term type is inferred from the return type of f
(or f0
) with terms as arguments. The coefficient type is computed by promoting the coefficient types of all AbstractLinear
arguments. In case f
(or f0
) returns a linear combination for term arguments, that coefficient type is also taken into account.
In order to catch all possible combinations of terms and linear combinations, @multilinear f
and @multilinear f f0
define a single new method f(x...; kw...)
that matches all argument types. (This is different from @linear
.) Hence, if f0
is not given, then the methods for f
that evaluate terms must have a non-generic signature. If instead the signature also is f(x::Any...)
, then this method is overwritten, resulting in an error when f
is called.
The new method defined by @multilinear
accepts all keyword arguments discussed for @linear
. Unknown keyword arguments are passed on to the call for term evaluation. The macro @linear_kw
works as for linear functions.
If the two-argument version of @multilinear
is used, then typically there is no other method for f
. Hence f
returns a linear combination for all arguments in this case. If all arguments are terms and also f0
returns a term, then the coefficient type is LinearCombinations.DefaultCoefftype
. For the one-argument version there must be at least one other method as discussed above. So f
may not return a linear combination for all arguments.
See also @linear
, @linear_kw
, LinearCombinations.DefaultCoefftype
.
Examples
Bilinear extension of a function returning a term
julia> f(x::Char, y::String) = x*y; @multilinear f
julia> a, b = Linear('x' => 1, 'y' => 2), Linear("z" => 1.0, "w" => -1.0)
(x+2*y, -w+z)
julia> f(a, "z")
2*yz+xz
julia> f('x', b)
-xw+xz
julia> f(a, b)
-xw+2.0*yz-2.0*yw+xz
Bilinear extension of a function returning a linear combination
julia> f(x::Char, y::String) = Linear(x*y => BigInt(1), y*x => BigInt(-1)); @multilinear f
julia> f(a, b) # same a and b as before
-2.0*zy-xw-zx+2.0*yz+wx-2.0*yw+xz+2.0*wy
julia> typeof(ans)
Linear{String, BigFloat}
Multilinear extension of a function
julia> g(xs::Union{Char,String}...) = *(xs...); @multilinear g
julia> g(a) # same a and b as before
x+2*y
julia> g(a, b)
-xw+2.0*yz-2.0*yw+xz
julia> g(a, b, a)
-xwx+xzx+4.0*yzy+2.0*xzy+2.0*yzx-2.0*ywx-2.0*xwy-4.0*ywy
Multilinear extension using the two-argument version of @multilinear
julia> @multilinear(h, *)
julia> h(a, b; coeff = 2) # same a and b as before
-2.0*xw+4.0*yz-4.0*yw+2.0*xz
LinearCombinations.MultilinearExtension
— TypeMultilinearExtension(f)
MultilinearExtension(f, name)
An element of this type is a multilinear extension of f
. One can additionally specify the name displayed for it.
Example
julia> a, b = Linear('x' => 1, 'y' => 2), Linear("z" => 1.0, "w" => -1.0)
(x+2*y, -w+z)
julia> const concat = MultilinearExtension(*, "concat")
concat
julia> concat(a, b)
-xw+2.0*yz-2.0*yw+xz
LinearCombinations.mul
— ConstantLinearCombinations.mul(x1::Any, x2::Any, ...)
Return the product of the arguments. This is the multilinear extension of *
.
The new name avoids type piracy since mul
accepts all argument types.
Common functionality
LinearCombinations.@linear_kw
— Macro@linear_kw function def
@linear_kw
scans a function definition for the keywords coefftype
, addto
, coeff
and sizehint
and makes them known to the LinearCombinations
package. This allows to write performant code. Not all keywords have to present. However, addto
and coeff
only have an effect if used together.
See also LinearCombinations.has_coefftype
, LinearCombinations.has_addto_coeff
, LinearCombinations.has_isfiltered
, LinearCombinations.has_sizehint
, LinearCombinations.unval
.
Example
Consider the following two functions:
f(x::Char) = Linear(uppercase(x) => 1, x => -1)
@linear f
using LinearCombinations: unval # unwraps a Val argument
@linear_kw function g(x::Char;
coefftype = Int,
addto = zero(Linear{Char,unval(coefftype)}),
coeff = 1)
addmul!(addto, uppercase(x), coeff)
addmul!(addto, x, -coeff)
addto
end
@linear g
The linear extensions are functionally equivalent, but g
will be much faster than f
.
julia> a = Linear('x' => 1, 'y' => 2)
x+2*y
julia> f(a; coefftype = Float64, coeff = 2)
4.0*Y-2.0*x-4.0*y+2.0*X
julia> g(a; coefftype = Float64, coeff = 2)
4.0*Y-2.0*x-4.0*y+2.0*X
Test whether keywords have been registered:
julia> using LinearCombinations: has_coefftype, has_addto_coeff, has_sizehint
julia> has_coefftype(g, Char), has_addto_coeff(g, Char), has_sizehint(g, Char)
(true, true, false)
LinearCombinations.DefaultCoefftype
— Typeconst LinearCombinations.DefaultCoefftype = Int
The coefficient type use by LinearCombinations
if no other coefficient type information is available.
LinearCombinations.linear_filter
— FunctionLinearCombinations.linear_filter(x) -> Bool
Return true
if the term x
is to be stored in linear combinations and false
if it is to be dropped.
The effect of this is that linear combinations don't live in the vector space (or free module) spanned by all possible terms, but rather in the quotient by the subspace (or submodule) spanned by the terms for which linear_filter
returns false
. Setting coefficients for terms that are divided out is allowed, but has no effect.
The default return value of linear_fiilter
is true
for all arguments, so that nothing is divided out.
See also keeps_filtered
, LinearCombinations.termcoeff
.
Example
julia> LinearCombinations.linear_filter(x::Char) = islowercase(x)
julia> a = Linear('x' => 1, 'Y' => 2)
x
julia> a['Z'] = 3
3
julia> a
x
LinearCombinations.keeps_filtered
— Functionkeeps_filtered(f, types...) -> Bool
Return true
if the following is satisfied, and false
otherwise: Whenever the function f
is called with arguments of types types
and returns a single term y
, then linear_filter(y) == true
holds.
By default, keeps_filtered
returns false
for all arguments. This can be changed to avoid unneccesary (and possibly expensive) calls to linear_filter
. Note that if f
returns a linear combination when called with term arguments, then all terms appearing in this linear combination satisfy the condition above anyway. The setting for keeps_filtered
doesn't matter in this case.
See also LinearCombinations.linear_filter
.
LinearCombinations.termcoeff
— FunctionLinearCombinations.termcoeff(xc::Pair) -> Pair
Transform a term-coefficient pair into the format that is stored in a linear combination.
This function is called for every term-coefficient pair that enters a linear combination. The default is to return a pair x => c
unchanged. This can be changed in special situations where one wants to normalize terms and coefficients. Note that termcoeff
is called only if linear_filter(x)
is true
.
See also linear_filter
.
Example
We show how to convert term-coefficient pairs with an uppercase letter to lowercase letters together with the negative coefficient.
julia> function LinearCombinations.termcoeff((x, c)::Pair{Char})
isuppercase(x) ? lowercase(x) => -c : x => c
end
julia> a = Linear('x' => 1, 'Y' => 2)
x-2*y
julia> a['y'], a['Y']
(-2, 2)
julia> a['Y'] = 3; a
x-3*y
julia> a + 'X'
-3*y
LinearCombinations.has_coefftype
— FunctionLinearCombinations.has_coefftype(f, types...) -> Bool
Return true
if the method for f
with signature given by types
is known to support the keyword argument coefftype
. The macro @linear_kw
is used to make this keyword known to the LinearCombinations package.
See also @linear_kw
, LinearCombinations.has_addto_coeff
, LinearCombinations.has_isfiltered
, LinearCombinations.has_sizehint
.
LinearCombinations.has_addto_coeff
— FunctionLinearCombinations.has_addto_coeff(f, types...) -> Bool
Return true
if the method for f
with signature given by types
is known to support the keyword arguments addto
and coeff
. The macro @linear_kw
is used to make these keywords known to the LinearCombinations package.
See also @linear_kw
, LinearCombinations.has_coefftype
, LinearCombinations.has_isfiltered
, LinearCombinations.has_sizehint
.
LinearCombinations.has_isfiltered
— FunctionLinearCombinations.has_isfiltered(f, types...) -> Bool
Return true
if the method for f
with signature given by types
is known to support the keyword argument is_filtered::Bool
. The macro @linear_kw
is used to make this keyword known to the LinearCombinations package.
The keyword argument is_filtered = true
for a linear or multilinear function f
indicates this potentially expensive test can be skipped when evaluating f
.
See also @linear_kw
, LinearCombinations.has_coefftype
, LinearCombinations.has_addto_coeff
, LinearCombinations.has_sizehint
, keeps_filtered
.
LinearCombinations.has_sizehint
— FunctionLinearCombinations.has_sizehint(f, types...) -> Bool
Return true
if the method for f
with signature given by types
is known to support the keyword argument sizehint
. The macro @linear_kw
is used to make this keyword known to the LinearCombinations package.
See also @linear_kw
, LinearCombinations.has_coefftype
, LinearCombinations.has_addto_coeff
, LinearCombinations.has_isfiltered
,
Switching between linear and multilinear maps
LinearCombinations.TensorSlurp
— TypeTensorSlurp(f)
TensorSlurp
turns a linear function acting on Tensor
terms into a multilinear function. This is similar to slurping in Julia.
The new function always returns a linear combination, even if none of the arguments is a linear combination. It recognizes all keyword arguments discussed for @linear
. Unknown keyword arguments are passed on to f
.
See also Tensor
, tensor
, TensorSplat
, @linear
.
Examples
We use swap
as an example of a function acting on tensors.
julia> const f = TensorSlurp(swap)
TensorSlurp(Regroup{(1, 2),(2, 1)})
julia> a, b = Linear('x' => 1, 'y' => 2), Linear("w" => 3, "z" => -1)
(x+2*y, 3*w-z)
julia> c = tensor(a, b)
-2*y⊗z+3*x⊗w-x⊗z+6*y⊗w
julia> swap(c)
3*w⊗x-z⊗x+6*w⊗y-2*z⊗y
julia> f(a, b)
3*w⊗x+6*w⊗y-z⊗x-2*z⊗y
julia> f(a, b; addto = swap(c), coeff = -1)
0
LinearCombinations.TensorSplat
— TypeTensorSplat(f)
TensorSplat
turns a multilinear function f
into a linear function acting on terms of type Tensor
. This is similar to splatting in Julia.
When called with an argument of type Tensor
, the new function returns the the value of f
on the components of the tensor (which may or may not be a linear combination). All keyword arguments are passed on to f
in this case.
When called with a linear combination as argument, the new function returns a linear combination. It recognizes all keyword arguments discussed for @linear
. Unknown keyword arguments are passed on to f
.
See also Tensor
, tensor
, TensorSlurp
, @linear
.
Examples
julia> const f = MultilinearExtension(*)
MultilinearExtension(*)
julia> const g = TensorSplat(f)
TensorSplat(MultilinearExtension(*))
julia> a, b = Linear('x' => 1, 'y' => 2), Linear("w" => 3, "z" => -1)
(x+2*y, 3*w-z)
julia> f(a, b)
3*xw-2*yz+6*yw-xz
julia> c = tensor(a, b)
-2*y⊗z+3*x⊗w-x⊗z+6*y⊗w
julia> g(c)
3*xw-2*yz+6*yw-xz
julia> g(c; addto = f(a, b), coeff = -1)
0