Skip to content

MOI.Utilities.operate StackOverflowError #2284

@hdavid16

Description

@hdavid16

The following returns a StackOverflowError due to deep recursion:

x = [i + i*MOI.VariableIndex(i) for i in 1:1000]
MOI.Utilities.operate(+, Int, x...)

operate is used in SolverAPI.jl to convert from SNF to SAF or SQF when possible.

The proposed fix is to rewrite the following

### 1c: operate(+, T, args...)
function operate(::typeof(+), ::Type{T}, f, g, h, args...) where {T<:Number}
return operate!(+, T, operate(+, T, f, g), h, args...)
end

as (credit to @bachdavi for this solution):

function operate(::typeof(+), ::Type{T}, args...) where {T<:Number}
    operate_plus(accum, x) = MOI.Utilities.operate!(+, T, accum, x)
    return reduce(operate_plus, args)
end

Some tests shown below (Note: operate2 is the new form of operate above):

# 100 ScalarAffineFunction args
julia> x = [i + i*MOI.VariableIndex(i) for i in 1:100];

julia> @time MOIU.operate(+, Int, x...);
  0.000200 seconds (199 allocations: 46.078 KiB)

julia> @time MOIU.operate2(+, Int, x...);
  0.000030 seconds (7 allocations: 6.797 KiB)
#--------------------------------------------------
# 1000 ScalarAffineFunction{Int} args  
julia> x = [i + i*MOI.VariableIndex(i) for i in 1:1000];

julia> @time MOIU.operate(+, Int, x...);
ERROR: StackOverflowError:

julia> @time MOIU.operate2(+, Int, x...);
  0.000093 seconds (9 allocations: 74.734 KiB) 
#--------------------------------------------------
# 1000 ScalarAffineFunction{Float64} args
julia> x = [i + i*MOI.VariableIndex(i) for i in 1.0:1000.0];

julia> @time MOIU.operate2(+, Float64, x...);
  0.000116 seconds (9 allocations: 74.734 KiB)
#--------------------------------------------------  
# 1000 ScalarQuadraticFunction args
julia> x = [i + i*MOI.VariableIndex(i)*MOI.VariableIndex(i) for i in 1:1000];

julia> @time MOIU.operate(+, Int, x...);
ERROR: StackOverflowError:

julia> @time MOIU.operate2(+, Int, x...);
  0.000105 seconds (5 allocations: 127.562 KiB)
#--------------------------------------------------  
# 1000 mixed aff and quad args
julia> x = [i + i*MOI.VariableIndex(i)*MOI.VariableIndex(i) for i in 1:500];

julia> y = [i + i*MOI.VariableIndex(i) for i in 1:500];

julia> z = vcat(x,y);

julia> @time MOIU.operate(+, Int, z...);
ERROR: StackOverflowError:

julia> @time MOIU.operate2(+, Int, z...);
  0.000126 seconds (6 allocations: 102.750 KiB)

Moving to higher number of affine function args:
image

Happy to submit a PR to MOI if desired.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions