Skip to content

Comparison with SparseAxisArray #16

@odow

Description

@odow

I was interested in how we compare with the default SparseAxisArray in JuMP.

A naive implementation, using flow = m[:flow] is terrible, because flow can't be typed correctly. If we use a function barrier, it's still a problem, because the in-liner removes the function barrier for some reason. But if you use @noinline, then things are nicer. (It's also the reason for the 8 sec in "Test dictionary".)

The filter introduces some function call overhead, and the alternative is slightly cheating, because it reduces to a single iteration through the keys of flow.

julia> test()
-- Test SparseAxisArray --
Variable creation: 0.027661869 [nv=2160]
Constraint creation: 0.519338562 [nc=9093]

-- Test dictionary --
Variable creation: 0.005746578 [nv=2160]
Constraint creation: 7.898748765 [nc=9093]

-- Test dictionary filter--
Variable creation: 0.005215764 [nv=2160]
Constraint creation: 0.705254153 [nc=9093]

-- Test dictionary alternative --
Variable creation: 0.005294635 [nv=2160]
Constraint creation: 0.013705239 [nc=9093]

-- Test sparse array slice--
Variable creation: 0.004835772 [nv=2160]
Constraint creation: 0.059691706 [nc=9093]

-- Test sparse array select --
Variable creation: 0.005393562 [nv=2160]
Constraint creation: 0.593879205 [nc=9093]

-- Test sparse array with cache --
Variable creation: 0.005129799 [nv=2160]
Constraint creation: 0.039051761 [nc=9093]

-- Test sparse array with cache (check empty) --
Variable creation: 0.005233172 [nv=2160]
Constraint creation: 0.037110871 [nc=2563]

-- Test indexed table --
Variable creation: 0.006558805 [nv=2160]
Constraint creation: 0.008344676 [nc=2482]

So the most interesting target is the sparse slice.

function create_vars_SparseAxisArray(m, pp)
    return @variable(
        m,
        flow[
            f = pp.factories,
            c = pp.customers,
            p = pp.products,
            t = pp.periods;
            haskey(pp.demand, (c, p, t)) && canproduce(pp, f, p)
        ] >= 0,
    )
end

function create_constraints_SparseAxisArray(m, pp)
    flow = m[:flow]
    _create_constraints_SparseAxisArray(m, pp, flow)
end

@noinline function _create_constraints_SparseAxisArray(m, pp, flow)
    # Production capacity
    for (f, t) in keys(pp.prodcap)
        @constraint(
            m,
            sum(
                flow[f, c, p, t] for
                (ff, c, p, tt) in eachindex(flow) if ff == f && tt == t
            )  pp.prodcap[f, t]
        )
    end

    # Customer demand
    for (c, p, t) in keys(pp.demand)
        @constraint(
            m,
            sum(
                flow[f, c, p, t] for
                (f, cc, pp, tt) in eachindex(flow) if cc == c && pp == p && tt == t
            )  pp.demand[c, p, t]
        )
    end

    # Transport capacity
    for (f, c) in keys(pp.flowcap), t in pp.periods
        @constraint(
            m,
            sum(
                flow[f, c, p, t] for
                (ff, cc, p, tt) in eachindex(flow) if ff == f && cc == c && tt == t
            )  pp.flowcap[f, c]
        )
    end
end

function test_SparseAxisArray(pp)
    println("-- Test SparseAxisArray --")
    m = Model()
    t1 = @elapsed create_vars_SparseAxisArray(m, pp)
    println("Variable creation: $t1 [nv=$(num_variables(m))]")

    t2 = @elapsed create_constraints_SparseAxisArray(m, pp)
    println(
        "Constraint creation: $t2 [nc=$(num_constraints(m, AffExpr, MOI.LessThan{Float64}))]",
    )
    return println()
end

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