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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions