# MutableArithmetics.jl

MutableArithmetics._rewriteFunction
_rewrite(
vectorized::Bool,
minus::Bool,
inner_factor,
current_sum::Union{Symbol, Nothing},
left_factors::Vector,
right_factors::Vector,
new_var::Symbol = gensym(),
)

Return new_var, code such that code is equivalent to

new_var = prod(left_factors) * inner_factor * prod(reverse(right_factors))

If current_sum is nothing, and is

new_var = current_sum op prod(left_factors) * inner_factor * prod(reverse(right_factors))

otherwise where op is + if !vectorized & !minus, .+ if vectorized & !minus, - if !vectorized & minus and .- if vectorized & minus.

source
MutableArithmetics.broadcast_mutabilityMethod
broadcast_mutability(T::Type, ::typeof(op), args::Type...)::MutableTrait

Return IsMutable to indicate an object of type T can be modified to be equal to broadcast(op, args...).

source
MutableArithmetics.copy_if_mutableMethod
copy_if_mutable(x)

Return a copy of x that can be mutated with MultableArithmetics's API without altering x. If mutability(x) is IsNotMutable then x is returned as none of x can be mutated. Otherwise, it redirects to mutable_copy. Mutable types should not implement a method for this function but should implement a method for mutable_copy instead.

source
MutableArithmetics.isequal_canonicalMethod
isequal_canonical(a, b)

Return whether a and b represent a same object, even if their representations differ.

Examples

The terms in two MathOptInterface affine functions may not match but once the duplicates are merged, the zero terms are removed and the terms are sorted in ascending order of variable indices (i.e. their canonical representation), the equality of the representation is equivalent to the equality of the objects begin represented.

source
MutableArithmetics.iszero!!Method
iszero!!(x)

Return a Bool indicating whether x is zero, possibly modifying x.

Examples

In MathOptInterface, a ScalarAffineFunction may contain duplicate terms. In Base.iszero, duplicate terms need to be merged but the function is left with duplicates as it cannot be modified. If iszero!! is called instead, the function will be canonicalized in addition for checking whether it is zero.

source
MutableArithmetics.mutable_copyFunction
mutable_copy(x)

Return a copy of x that can be mutated with MultableArithmetics's API without altering x.

Examples

The copy of a JuMP affine expression does not copy the underlying model as it cannot be modified though the MultableArithmetics's API, however, it calls copy_if_mutable on the coefficients and on the constant as they could be mutated.

source
MutableArithmetics.operateFunction
operate(op::Function, args...)

Return an object equal to the result of op(args...) that can be mutated through the MultableArithmetics API without affecting the arguments.

By default:

• operate(+, x) and operate(+, x) redirect to copy_if_mutable(x) so a mutable type T can return the same instance from unary operators +(x::T) = x and *(x::T) = x.
• operate(+, args...) (resp. operate(-, args...) and operate(*, args...)) redirect to +(args...) (resp. -(args...) and *(args...)) if length(args) is at least 2 (or the operation is -).

Note that when op is a Base function whose implementation can be improved for mutable arguments, operate(op, args...) may have an implementation in this package relying on the MutableArithmetics API instead of redirecting to op(args...). This is the case for instance:

• for Base.sum,
• for LinearAlgebra.dot and
• for matrix-matrix product and matrix-vector product.

Therefore, for mutable arguments, there may be a performance advantage to call operate(op, args...) instead of op(args...).

Example

If for a mutable type T, the following is defined:

function Base.:*(a::Bool, x::T)
return a ? x : zero(T)
end

then operate(*, a, x) will return the instance x whose modification will affect the argument of operate. Therefore, the following method need to be implemented

function MA.operate(::typeof(*), a::Bool, x::T)
return a ? MA.mutable_copy(x) : zero(T)
end
source
MutableArithmetics.operate_to!Method
operate_to!(output, op::Function, args...)

Modify the value of output to be equal to the value of op(args...). Can only be called if mutability(output, op, args...) returns IsMutable.

If output === args[i] for some i, this function may throw an error. Use operate!! or operate! instead.

For example, in DynamicPolynomials, operate_to!(p, +, p, q) throws an error because otherwise, the algorithm would fill p while iterating over the terms of p and q hence it will never terminate. On the other hand operate!(+, p, q) uses a different algorithm that efficiently inserts the terms of q in the sorted list of terms of p with minimal displacement.

source
MutableArithmetics.promote_operationMethod
promote_operation(op::Function, ArgsTypes::Type...)

Returns the type returned to the call operate(op, args...) where the types of the arguments args are ArgsTypes.

source
MutableArithmetics.rewrite_generatorMethod
rewrite_generator(expr::Expr, inner::Function)

Rewrites the generator statements expr and returns a properly nested for loop with nested filters as specified.

Examples

julia> using MutableArithmetics

julia> MutableArithmetics.rewrite_generator(:(i for i in 1:2 if isodd(i)), i -> :($i + 1)) :(for$(Expr(:escape, :(i = 1:2)))
if \$(Expr(:escape, :(isodd(i))))
i + 1
end
end)
source
MutableArithmetics.@rewriteMacro
@rewrite(expr)

Return the value of expr exploiting the mutability of the temporary expressions created for the computation of the result.

Examples

The expression

MA.@rewrite(x + y * z + u * v * w)

is rewritten into

MA.add_mul!!(
u, v, w)