MutableArithmetics.jl

MutableArithmetics._broadcasted_typeMethod

This method is a generic fallback for array types that are not DefaultArrayStyle. Because we can't tell the container from a generic broadcast style, we fallback to Any, which is always a valid super type (just not a helpful one).

In MutableArithmetics, _broadcasted_type appears only in promote_broadcast, which itself appears only in broadcast_mutability, and so types hitting this method will fallback to the IsNotMutable() branch, which is the expected outcome.

source
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._rewrite_genericFunction
_rewrite_generic(stack::Expr, expr::T)::Tuple{Any,Bool}

This method is the heart of the rewrite logic. It converts expr into a mutable equivalent, places any intermediate calculations onto stack, and returns a tuple containing the return value–-which is either expr or a gensymed variable equivalent to expr–-and a boolean flag that indicates whether the return value can be mutated by future callers.

source
MutableArithmetics._rewrite_genericMethod
_rewrite_generic(::Expr, x)

A generic fallback. Given a type x we return it without mutation. In addition, this type should not be mutated by future callers.

source
MutableArithmetics._rewrite_genericMethod
_rewrite_generic(::Expr, x::Number)

If x is a Number at macro expansion time, it must be a constant literal. We return x without mutation, but we return true because other callers may mutate the value without fear. Put aother way, they don't need to wrap the value in copy_if_mutable(x) before using it as the first argument to operate!!.

This most commonly happens in situations like x^2.

source
MutableArithmetics.broadcast!Function
broadcast!(op::Function, args...)

Modify the value of args[1] to be equal to the value of broadcast(op, args...).

This method can only be called if mutability(args[1], op, args...) returns IsMutable.

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.buffered_operate!Function
buffered_operate!(buffer, op::Function, args...)

Modify the value of args[1] to be equal to the value of op(args...), possibly modifying buffer. Can only be called if mutability(args[1], op, args...) returns IsMutable.

source
MutableArithmetics.buffered_operate_to!Method
buffered_operate_to!(buffer, output, op::Function, args...)

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

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!Method
operate!(op::Function, args...)

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

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.rewriteMethod
rewrite(expr; move_factors_into_sums::Bool = true) -> Tuple{Symbol,Expr}

Rewrites the expression expr to use mutable arithmetics.

Returns (variable, code) comprised of a gensym'd variable equivalent to expr and the code necessary to create the variable.

move_factors_into_sums

If move_factors_into_sums = true, some terms are rewritten based on the assumption that summations produce a linear function.

For example, if move_factors_into_sums = true, then y * sum(x[i] for i in 1:2) is rewritten to:

variable = MA.Zero()
for i in 1:2
    variable = MA.operate!!(MA.add_mul, result, y, x[i])
end

If move_factors_into_sums = false, it is rewritten to:

term = MA.Zero()
for i in 1:2
    term = MA.operate!!(MA.add_mul, term, x[i])
end
variable = MA.operate!!(*, y, term)

The latter can produce an additional allocation if there is an efficient fallback for add_mul and not for *(y, term).

source
MutableArithmetics.rewrite_and_returnMethod
rewrite_and_return(expr; move_factors_into_sums::Bool = true) -> Expr

Rewrite the expression expr using mutable arithmetics and return an expression in which the last statement is equivalent to expr.

See rewrite for an explanation of the keyword argument.

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, move_factors_into_sums = true)

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

If you have an Expr as input, use rewrite_and_return instead.

See rewrite for an explanation of the keyword argument.

Info

Passing move_factors_into_sums after a ; is not supported. Use a , instead.

source