Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
hakank opened this issue Dec 12, 2020 · 7 comments · Fixed by #221
Open
Labels
branching strategy enhancement New feature or request

Comments

@hakank
Copy link

hakank commented Dec 12, 2020

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 Wikunia added the bug Something isn't working label Dec 12, 2020
@Wikunia
Copy link
Owner

Wikunia commented Dec 12, 2020

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 Wikunia linked a pull request Dec 12, 2020 that will close this issue
@Wikunia
Copy link
Owner

Wikunia commented Dec 12, 2020

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
Copy link
Owner

Wikunia commented Dec 12, 2020

I found out why this is very slow in general:

  • The reified constraint doesn't anti prune see Anti prune reified for linear constraints #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 Activity based search #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 😄

@Wikunia Wikunia reopened this Dec 12, 2020
@hakank
Copy link
Author

hakank commented Dec 12, 2020

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
Copy link
Owner

Wikunia commented Dec 13, 2020

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 Wikunia added enhancement New feature or request and removed bug Something isn't working labels Dec 13, 2020
@hakank
Copy link
Author

hakank commented Dec 13, 2020

@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
Copy link
Owner

Wikunia commented Dec 13, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
branching strategy enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants