ConstraintSolver.jl icon indicating copy to clipboard operation
ConstraintSolver.jl copied to clipboard

@objective in a (specific) model give "MethodError: no method matching iterate(::Nothing)"

Open hakank opened this issue 5 years ago • 7 comments

Here is a model that works without @objective but adding an @objective throws MethodError: no method matching iterate(::Nothing). I don't know is this is a bug in my model or somewhere else. (It use a decomposition of cumulative and that might very well be the culprit.)

And sorry about the largish model. I haven't had any similar problems with @objective before this.

function cumulative(model, start, duration, resource, limit)
    tasks = [i for i in 1:length(start) if resource[i] > 0 && duration[i] > 0]
    num_tasks = length(tasks)
    times_min_a = round.(Int,[JuMP.lower_bound(start[i]) for i in tasks])
    times_min = minimum(times_min_a)
    times_max_a = round.(Int,[JuMP.upper_bound(start[i])+duration[i] for i in tasks])
    times_max = maximum(times_max_a)
    for t in times_min:times_max
        bs = @variable(model, [1:num_tasks], Bin)
        bt = @variable(model, [1:num_tasks], Bin)
        b  = @variable(model, [1:num_tasks], Bin) 
        for i in tasks
            # is this task active during this time t?
            @constraint(model, bs[i] := {start[i] <= t})
            @constraint(model, bt[i] := {t <= start[i]+duration[i]-1}) # (should be '<')
            @constraint(model, b[i] := { bs[i] + bt[i] == 2})
        end
        # Check that there's no conflicts in time t
        @constraint(model,sum([b[i]*resource[i] for i in tasks]) <= limit)
  end

end

function furniture_moving1(print_solutions=true,all_solutions=false)

    cbc_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
    glpk_optimizer = optimizer_with_attributes(GLPK.Optimizer)
    ipopt_optimizer = optimizer_with_attributes(Ipopt.Optimizer)

    model = Model(optimizer_with_attributes(CS.Optimizer,  
                                                            "logging"=>[],

                                                            "traverse_strategy"=>:BFS,
                                                            "branch_split"=>:InHalf, # <-
                                                            "time_limit"=>6,

                                                            # "lp_optimizer" => cbc_optimizer,
                                                            "lp_optimizer" => glpk_optimizer,
                                                            # "lp_optimizer" => ipopt_optimizer,
                                        ))

    # Furniture moving problem
    n = 4
    # [piano, chair, bed, table]
    durations = [30,10,15,15]
    resources = [3,1,3,2] # people needed per task
    @variable(model, 0 <= start_times[1:n] <= 60, Int)
    @variable(model, 0 <= end_times[1:n]   <= 60, Int)
    @variable(model, 1 <= limit <= 3, Int)

    for i in 1:n
        @constraint(model,end_times[i] == start_times[i] + durations[i])
    end
    cumulative(model, start_times, durations, resources, limit)

    # This throws: MethodError: no method matching iterate(::Nothing)
    # @objective(model,Min,limit)

    optimize!(model)

    status = JuMP.termination_status(model)
    if status == MOI.OPTIMAL
        num_sols = MOI.get(model, MOI.ResultCount())
        println("num_sols:$num_sols\n")
        if print_solutions
            for sol in 1:num_sols
                println("solution #$sol")
                start_timesx = convert.(Integer,JuMP.value.(start_times; result=sol))
                end_timesx = convert.(Integer,JuMP.value.(end_times; result=sol))
                max_timex = convert.(Integer,JuMP.value.(max_time; result=sol))
                limitx = convert.(Integer,JuMP.value.(limit; result=sol))
                println("start_times:$start_timesx")
                println("durations  :$durations")
                println("end_times  :$end_timesx")
                println("resources  :$resources")
                println("limit      :$limitx")
            end
        end
    end
end

@time furniture_moving1()

hakank avatar Dec 12 '20 11:12 hakank

Sorry that is a small error on my side. Good catch. A fix is on its way. Unfortunately my solver has quite some troubles solving it at the moment.Not sure why yet but I see that I need more testing with reified constraints for objective functions.

Thanks again for the issue and the testing of my solver. It's a long way to go to make it really usable as it's a massive project but I enjoy the journey.

Wikunia avatar Dec 12 '20 12:12 Wikunia

The optimal solution is limit = 3, right? I'm a bit confused why it takes so long when optimizing. When setting limit to <= 2 it's immediately infeasible for obvious reasons but the bound computed is always 1.0 when optimizing. I'll check this out in the next days but most likely not today.

Wikunia avatar Dec 12 '20 12:12 Wikunia

I found out why this is very slow in general:

  • The reified constraint doesn't anti prune see #222
  • It's not immediately obvious that the sum over the b for each task needs to be the duration, so I would add that constraint
  • My branching strategy fails miserably in this case as it tries out various variations of the binary variables but not of start_times and end_times. I'm working on activity based branch (see #181) but that also seems to fail here as the activity for the binary variables is much higher. I think it would be valuable to have a strategy which counts the activity as how many values can be removed by this change instead.

Nevertheless in general a scheduling constraint needs to be implemented :smile:

Wikunia avatar Dec 12 '20 19:12 Wikunia

Thanks for looking into this, Ole.

And I agree that there should be a dedicated scheduling constraint. :-)

However, I don't understand your comment: "It's not immediately obvious that the sum over the b for each task needs to be the duration, so I would add that constraint.". Would you mind explain a little more what you mean? (The approach for this implementation is to - for each time step - check that the the total number of active resources cannot exceed limit. "active" is here the complete duration of a task, from start to start + duration -1. )

hakank avatar Dec 12 '20 20:12 hakank

For clarification: The b basically encodes whether it's active in the given time. We want that every task is completed until our maximum end time. This means that when we have a vector of b for each task the sum of it over t has to equal the duration.

This is given by your definition but it's not as easy for my solver to see. I would therefore add that constraint as a user.

Wikunia avatar Dec 13 '20 08:12 Wikunia

@Wikunia Yes, you have a good point and I will test this as well. (My formulation is one of the most used decompositions in the CP folk lore.)

hakank avatar Dec 13 '20 09:12 hakank

Unfortunately because of the other problems I have identified this will still take a minute to solve. Even with #222 because of the branching strategy. I'll do more research on that.

Wikunia avatar Dec 13 '20 09:12 Wikunia