Skip to content

Conversation

blegat
Copy link
Member

@blegat blegat commented Mar 14, 2021

Current benchmark:

import MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities
import Clp
function bench(n, m)
    model = MOIU.Model{Float64}()
    x = MOI.add_variables(model, n)
    fx = MOI.SingleVariable.(x)
    ofx = 1.0 .* fx
    for i in 1:n
        MOI.add_constraint(model, ofx[i], MOI.LessThan(1.0))
        MOI.add_constraint(model, ofx[i], MOI.GreaterThan(1.0))
    end
    s = sum(ofx)
    for i in 1:n
        MOI.add_constraint(model, s, MOI.EqualTo(1.0))
    end
    optimizer = MOI.Bridges.full_bridge_optimizer(
        MOIU.CachingOptimizer(
            M(),
            Clp.Optimizer(),
        ),
        Float64,
    )
    @time MOI.copy_to(optimizer, model)
    @time MOIU.attach_optimizer(optimizer.model)
    optimizer.model
end

With this branch, Cbc#od/moi10 and M=MOIU.Model{Float64}, I get:

julia> bench(1000, 1000);
  0.035744 seconds (18.71 k allocations: 32.124 MiB)
  0.075348 seconds (429 allocations: 42.859 MiB)

With this branch, Cbc#bl/matrix and M=Cbc.Model, I get:

julia> bench(1000, 1000);
  0.011251 seconds (698 allocations: 551.617 KiB)
  0.000164 seconds (105 allocations: 146.023 KiB)

What really matters is the sum of the two. The speedup is approximately 10x.
We already have a significant decrease but I think we could have a number of allocation which does not scale linearly with n and m so I'm trying to figure it out.
The number of allocations is a constant + some logarithmic on n and m which is expected (e.g. because of push in caches).

See #1261

Requires #1267, #1286

@blegat blegat force-pushed the bl/pass_cons branch 2 times, most recently from dd03250 to 6206427 Compare March 21, 2021 16:35
Base automatically changed from bl/pass_cons to master March 21, 2021 21:38
@blegat blegat force-pushed the bl/matrix_of_constraints branch from 1a42cb5 to 9b3166b Compare March 21, 2021 23:19
@blegat blegat marked this pull request as ready for review March 22, 2021 21:17
@odow
Copy link
Member

odow commented Apr 19, 2021

What's the status of this? Can we make a new MOI release without it?

@blegat
Copy link
Member Author

blegat commented Apr 19, 2021

There is still some work needed, we don't need to wait for it for a new MOI release

@blegat blegat force-pushed the bl/matrix_of_constraints branch from 235c46a to 3565b31 Compare May 2, 2021 17:20
@blegat blegat mentioned this pull request May 3, 2021
7 tasks
@blegat blegat force-pushed the bl/matrix_of_constraints branch 2 times, most recently from 8cf4d33 to ea094b7 Compare May 4, 2021 07:14
@blegat blegat mentioned this pull request May 5, 2021
@blegat blegat force-pushed the bl/matrix_of_constraints branch from ea094b7 to 38da881 Compare May 9, 2021 07:28
@odow
Copy link
Member

odow commented May 9, 2021

As another benchmark: how is the first solve? Compilation times are our big killer for this.

@blegat blegat force-pushed the bl/matrix_of_constraints branch 2 times, most recently from f3a2afb to 866bca9 Compare May 9, 2021 22:48
@blegat blegat force-pushed the bl/matrix_of_constraints branch 3 times, most recently from c875104 to 5edfaaf Compare May 10, 2021 11:38
Copy link
Member

@odow odow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite hard to review because there is a lot of new types being added

  • ProductOfSets
  • MixOfScalarSets
  • OrderedProductOfSets
  • OrderedProductOfScalarSets
  • UnevenIterator
  • Box
  • MatrixOfConstraints
  • IdentityMap
  • MutableSparseMatrixCSC

Can we break it down into smaller pieces? It's not obvious why we need so many different structures, or how they inter-relate.

single_variable_not_added
end

function final_touch(::MOI.ModelLike, idxmap) end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a docstring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know what you think for the name. Another option would be end_of_copy

if model.variable_indices !== nothing
push!(model.variable_indices, vi)
end
_add_variable(model.constraints)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to force other types to overload _add_variable. Why not just overload MOI.add_variable(model.constraints)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then VectorOfConstraints should return a VariableIndex but currently it does not keep track of this information.

@blegat blegat force-pushed the bl/matrix_of_constraints branch from 5edfaaf to 71d0a8b Compare May 12, 2021 10:52
@blegat blegat mentioned this pull request May 12, 2021
@blegat blegat force-pushed the bl/matrix_of_constraints branch from aa37bf9 to 82e94cd Compare May 14, 2021 04:08
Copy link
Member

@odow odow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs documentation added to the Utilities part of the manual.

I wonder about making a subdirectory in src/Utilities to contain these. That would isolate the code a little more, and better document what goes with what.

I'm also keen to see what impact this has on our time-to-first-solve issue.

return new{F,S}(MOIU.VectorOfConstraints{F,S}())
end
end
function MOI.Utilities._add_variable(::OnlyCopyConstraints) end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a public API that people need to implement, it shouldn't begin with _.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be implemented by the type of the constraints field but it's currently not public API.
It's best we don't let user create new types at the moment until we converge. This is similar to _delete_variables, _deleted_constraints and _throw_if_cannot_delete.

end

function final_touch(model::MatrixOfConstraints, index_map)
num_rows = number_of_rows(model.sets)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs documenting

function final_touch(model::MatrixOfConstraints, index_map)
num_rows = number_of_rows(model.sets)
resize!(model.constants, num_rows)
set_number_of_rows(model.coefficients, num_rows)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs documenting

Return an integer corresponding to the index of the set type in the list given
by [`set_types`](@ref)..
"""
set_index(sets::ProductOfSets, ::Type{S}) where {S<:MOI.AbstractSet} = nothing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be documented in MatrixOfConstraints

@blegat blegat force-pushed the bl/matrix_of_constraints branch from 40ced0d to e806115 Compare May 14, 2021 15:39
@blegat blegat force-pushed the bl/matrix_of_constraints branch from e806115 to b629e45 Compare May 14, 2021 19:08
@blegat blegat mentioned this pull request May 15, 2021
@blegat blegat requested a review from odow May 18, 2021 00:10
Copy link
Member

@odow odow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some reservations about adding yet more complexity to MOI. And it would be better if the documentation clearly identified which functions needed to be implemented for which bits, but it may be easier to merge and keep iterating in new PRs. We still have time to break things, so we should merge and then update solvers in branches to figure out what tweaks need to be made.

type_def = :(struct $esc_name{$T} <: $MOIU.OrderedProductOfScalarSets{$T}
num_rows::Vector{Int}
function $esc_name{$T}() where {$T}
return new(zeros(Int, $(1 + length(set_types))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So all this is a OrderedProductOfSets but without the dimension field? Then the only efficiency is that we save a Dict{Int,Int}() call, and a call on empty!?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that not so much. I used to store the dimension for the scalar sets for OrderedProductOfSets so OrderedProductOfScalarSets was a big win but now even OrderedProductOfSets don't store the dimension of scalar sets so we could indeed get rid of OrderedProductOfScalarSets (although it's just a few lines of code).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of this is that it's just a few lines of code, but much more code to compile.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we can remove it


```@docs
Utilities.ProductOfSets
Utilities.set_index
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions (set_index, set_types, add_set, indices) get called by MatrixOfConstraints, so they're part of that API. ProductOfSets is just one implementation of the .sets field.

Is any solver going to implement a different type for the .sets field? I wonder if we're making our life harder by making this design too flexible...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this into a well-defined API to make the code more understandable. We will need yet another one for list of sets that are not known at compile time, e.g., DiffOpt but we might have enough with 3, can't say for now.

@blegat
Copy link
Member Author

blegat commented May 18, 2021

And it would be better if the documentation clearly identified which functions needed to be implemented for which bits, but it may be easier to merge and keep iterating in new PRs. We still have time to break things, so we should merge and then update solvers in branches to figure out what tweaks need to be made.

Yes I agree. It's ready so that people can start play with it and we can iterate a bit before releasing.

@blegat blegat merged commit 7006a17 into master May 18, 2021
@blegat blegat deleted the bl/matrix_of_constraints branch May 18, 2021 03:51
@blegat blegat added this to the v0.10 milestone May 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants