Wikunia / ConstraintSolver.jl

ConstraintSolver in Julia: Blog posts ->
https://opensourc.es/blog/constraint-solver-1
MIT License
136 stars 13 forks source link

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

Open hakank opened 3 years ago

hakank commented 3 years ago

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()
Wikunia commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

I found out why this is very slow in general:

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

hakank commented 3 years ago

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. )

Wikunia commented 3 years ago

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.

hakank commented 3 years ago

@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.)

Wikunia commented 3 years ago

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.