Julia with Knitro

Hey everyone! I’m trying to set a time limit and a gap in Julia using Knitro, but not having success:

m = Model(KNITRO.Optimizer)
set_optimizer_attribute(m, “maxtime_cpu”, 3.6e+2)
set_optimizer_attribute(m, “mip_opt_gap_abs”, 1.0e+0)

ERROR: MathOptInterface.UnsupportedAttribute

Does anyone know how to set these parameters?

Thanks in advance!

Hello and welcome. I’ve moved this to the Optimization area to get more :eyes: on it.

This specifically can be set using a function from JuMP itself:
set_time_limit_sec(m, 3.6e+2)
where the value is in seconds.

ERROR: MathOptInterface.UnsupportedAttribute

What is the full error? Does it happen with maxtime_cpu or mip_opt_gap_abs?

Thanks!

I tried to do it this way, but the runtime remains longer than what was set in parameter :frowning:

Sorry! It’s just happening with the maxtime_cpu. For mip_opt_gap_abs, model processing is not interrupted when it reaches the established gap.

Hi, I’m trying this but the set_time_limit_sec is completely ignored. I’m also using InfiniteOpt.jl, can this be the reason?

Hi, I’m trying this but the set_time_limit_sec is completely ignored

Do you have a reproducible example?

InfiniteOpt.jl just passes the set_time_limit_sec call directly to the underlying JuMP model (referred to as the optimizer_model): https://github.com/infiniteopt/InfiniteOpt.jl/blob/f0929c6990c18b6bfe2adb2384146074072b9466/src/optimize.jl#L249-L263

From my experience, KNITRO and other solvers will not exactly terminate at the time limit since they only check the wall-time in-between iterations. It is hard to say more without seeing the your code.

2 Likes

Sorry no MWE, the full program has like 2000 lines.
Some pseudo-code goes like this:

using InfiniteOpt, JuMP, KNITRO

function solvePolicies(optimizer, # model optimizer
    sets::modelSettings, # number of EVs, discrete time supports, etc.
    data::Dict, # information for the model (mainly parameters), these are the devices (EV, BESS, PV, etc.), the costs (interests, capex, etc) and the revenues
    )
    # Sets
    tend=sets.dTime[end]
    t0=sets.dTime[1]; # initial time, can´t divide by 0

    model = InfiniteModel(optimizer) # create model
    # KNITRO attributes
    set_optimizer_attributes(model, 
                            "feastol" => 1e-3,
                            "mip_multistart"=> 1,
                            "mip_outlevel"=> 2,
                            "mip_method"=> 1, 
                            "mip_heuristic_maxit"=>800,
                            )
    set_time_limit_sec(model, 60*15);

    # define cont t
    @infinite_parameter(model, t in [t0, tend], supports = collect(sets.dTime), derivative_method = FiniteDifference(Forward(),true))

    # Add variables, constraints, obj
    model = build_model(model, sets, data); # <== this is were the magic happens
    # Solve model
    optimize!(model)
    results = getResults(model) # save results
    MOI.empty!(InfiniteOpt.backend(model)) # clean MOI
    return results;
end;

function getResults(model)
  x = all_variables(model)
  xdf=DataFrame(
      name = name.(x),
      value = value.(x),
  )
  idx=findall(x->x=="",name.(x)) # find point variables
  delete!(xdf, idx) # delete point variables
  xdict=Dict{Any,Any}(Pair.(xdf.name, xdf.value))
  merge!(xdict,Dict("t"=>supports(model[:t]), "γ_cont"=>value.(model[:γ_cont])))
  return xdict
end

function rollingHorizon(W, # weights
    Tw, # time window [days]
    steps, # number of steps to move the window
    Δt) # time length of each step [hr]
    # Build the data dictionary
    
    data=buildDict();
    # Initialize
    tend = 24*Tw; # [hr]
    Dt=0:Δt:tend; Dt=Dt*3600; # time array in seconds
    s=modelSettings(nEV=1:2, dTime=collect(Dt), costWeights=W);
    results=Vector{Dict}(undef, steps); # allocate memory
    for ts in 1:steps
        # build+solve model
        model=solvePolicies(KNITRO.Optimizer, s, data);
        # Print solution summary
        # solution_summary(optimizer_model(model))
        results[ts]=getResults(model);
        model=InfiniteModel();
        # update data, which contains the initial conditions
        data=update_measurements(results[ts], s, data);
        # move time window
        Dt = Dt .+ Δt*3600.0;
        # update_forecasts.(data, Dt)
        s.dTime=collect(Dt);
    end
    return results, data, s
end

Wgrid = 1;
Wloss = 2;
W=[Wgrid 1000 Wloss];
results, data, s=rollingHorizon(W, 1, 4, 1/4);
1 Like

And what happens? Does each solvePolicies call take longer than 15 minutes to solve?

Yes, the call to set_time_limit_sec is ignored basically. I also tried maxtime_cpu and maxtime_time and same luck.
I have calls that take like 500ms and calls that take hours, depending on the timestep.

Hi! any update on this topic?
Should I raise an issue at the KNITRO.jl repo?

Can you try with the latest version of KNITRO.jl (v0.14.1)?

I recently made a number of improvements.

If it still persists, can you provide the full output log? What is KNITRO doing after the time limit hits?

It’s going to be hard to debug this without a reproducible example.

Should I raise an issue at the KNITRO.jl repo?

No need. It’ll just be me replying over there.

2 Likes

Yeah sure, this is my ] status

(myProject) pkg> status
Status `D:\OneDrive\Simulation\Project.toml`
  [336ed68f] CSV v0.10.12
  [13f3f980] CairoMakie v0.11.5
  [35d6a980] ColorSchemes v3.24.0
  [a93c6f00] DataFrames v1.6.1
  [31c24e10] Distributions v0.25.107
  [e9467ef8] GLMakie v0.9.5
  [cc18c42c] GaussianMixtures v0.3.8
  [87dc4568] HiGHS v1.8.0
  [20393b10] InfiniteOpt v0.5.8
⌅ [a98d9a8b] Interpolations v0.14.7
  [b6b21f68] Ipopt v1.6.0
  [c8e1da08] IterTools v1.10.0
  [0f8b85d8] JSON3 v1.14.0
  [4076af6c] JuMP v1.18.1
  [67920dd8] KNITRO v0.14.1
  [b964fa9f] LaTeXStrings v1.3.1
  [a5b28938] LiiBRA v0.3.4
  [23992714] MAT v0.10.6
  [ee78f7c6] Makie v0.20.4
  [b8f27783] MathOptInterface v1.25.1
  [01bcebdf] Nonconvex v2.1.3
  [bf347577] NonconvexIpopt v0.4.2
  [b43a31b8] NonconvexNLopt v0.1.8
  [d96e819e] Parameters v0.12.3
  [ccf2f8ad] PlotThemes v3.1.0
  [995b91a9] PlotUtils v1.4.0
  [f0f68f2c] PlotlyJS v0.18.12
  [91a5bcdd] Plots v1.40.0
  [c46f51b8] ProfileView v1.7.2
  [295af30f] Revise v3.5.13
  [99342f36] StateSpaceModels v0.6.7
⌅ [2913bbd2] StatsBase v0.33.21
  [f3b207a7] StatsPlots v0.15.6
  [9e3dc215] TimeSeries v0.24.1
  [fdbf4ff8] XLSX v0.10.1
  [37e2e46d] LinearAlgebra
  [9a3f8284] Random
  [10745b16] Statistics v1.10.0

and I´m running my model with the following model attributes

set_optimizer_attributes(model, 
                            "tuner"=>1,
                            "scale"=>1, # 
                            "feastol" => 1e-3,
                            "mip_multistart"=> 1,
                            "mip_outlevel"=> 2,
                            "mip_method"=> 1,
                            "mip_numthreads"=> 3,
                            "mip_heuristic_maxit"=>800,
                            "mip_maxnodes" => 700,
                            )
    set_time_limit_sec(model, 60);

The optimization violates the time limit of 60s.
It does however respect my "mip_maxnodes" => 700.
I don’t know what the issue might be.
Sorry that I don’t have a MWE but the issue is hard to reproduce since I’m solving a MINLP in a rolling horizon window. So depending on the model inputs optimize! can take values between 10ms or 2hs (stopping due to the MIP node limit).

What happens if you set the time limit in set_optimizer_attributes instead of calling set_time_limit_sec?

"maxtime_cpu" => 60.0 or "maxtime_real" => 60.0

well, weirdly enough, the first "maxtime_cpu => 60.0" got me the following error:

ERROR: MathOptInterface.UnsupportedAttribute{MathOptInterface.RawOptimizerAttribute}: Attribute MathOptInterface.RawOptimizerAttribute("maxtime_cpu") is not supported by the model.
Stacktrace:
  [1] set(model::KNITRO.Optimizer, attr::MathOptInterface.RawOptimizerAttribute, value::Float64)
    @ KNITRO C:\Users\user\.julia\packages\KNITRO\4sI4C\src\MOI_wrapper.jl:316
  [2] set
    @ C:\Users\user\.julia\packages\MathOptInterface\3JqTJ\src\Bridges\bridge_optimizer.jl:955 [inlined]
  [3] set(model::MathOptInterface.Utilities.CachingOptimizer{…}, attr::MathOptInterface.RawOptimizerAttribute, value::Float64)
    @ MathOptInterface.Utilities C:\Users\user\.julia\packages\MathOptInterface\3JqTJ\src\Utilities\cachingoptimizer.jl:1059
  [4] set(m::Model, attr::MathOptInterface.RawOptimizerAttribute, value::Float64)
    @ JuMP C:\Users\user\.julia\packages\JuMP\027Gt\src\optimizer_interface.jl:728
  [5] set_attribute
    @ C:\Users\user\.julia\packages\JuMP\027Gt\src\optimizer_interface.jl:957 [inlined]
  [6] set_attribute
    @ C:\Users\user\.julia\packages\JuMP\027Gt\src\optimizer_interface.jl:966 [inlined]
  [7] set_optimizer_attribute
    @ C:\Users\user\.julia\packages\JuMP\027Gt\src\optimizer_interface.jl:77 [inlined]
  [8] set_optimizer_attribute
    @ C:\Users\user\.julia\packages\InfiniteOpt\WWtLl\src\optimize.jl:309 [inlined]
  [9] set_optimizer_attributes
    @ C:\Users\user\.julia\packages\InfiniteOpt\WWtLl\src\optimize.jl:356 [inlined]
Some type information was truncated. Use `show(err)` to see complete types.

The second one is running now. Let’s see if I can trigger something

And unfortunately its also ignored.
Here’s the report of one my runs:

=======================================
           Academic License
       (NOT FOR COMMERCIAL USE)
         Artelys Knitro 13.2.0
=======================================

MINLP solver shifted start point to satisfy bounds (291 variables).
WARNING: Problem appears to have nonlinear equalities and be non-convex.
         The Knitro mixed integer solver is designed for convex problems.
         For non-convex problems it is only a heuristic, and the reported
         bounds and optimality claims cannot be verified.

datacheck:               0
feastol:                 0.001
hessian_no_f:            1
maxtime_real:            1
numthreads:              1
outappend:               1
mip_method:              1
mip_multistart           1
mip_numthreads:          1
mip_terminate:           1
Knitro changing mip_rootalg from AUTO to 1.
Knitro changing mip_lpalg from AUTO to 3.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 1.
Knitro changing mip_gomory from AUTO to 0.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics
-----------------------
Objective goal:  Minimize
Objective type:  quadratic
Number of variables:                               3880
    bounded below only:                             388
    bounded above only:                             388
    bounded below and above:                       1358
    fixed:                                            0
    free:                                          1746
Number of binary variables:                         388
Number of integer variables:                          0
Number of constraints:                             3589
    linear equalities:                             2231
    quadratic equalities:                             0
    gen. nonlinear equalities:                      582
    linear one-sided inequalities:                  776
    quadratic one-sided inequalities:                 0
    gen. nonlinear one-sided inequalities:            0
    linear two-sided inequalities:                    0
    quadratic two-sided inequalities:                 0
    gen. nonlinear two-sided inequalities:            0
Number of nonzeros in Jacobian:                    9911
Number of nonzeros in Hessian:                     2328

Knitro detected 0 GUB constraints
Knitro derived 0 knapsack covers after examining 776 constraints
Knitro using Branch and Bound method with 1 thread.

       Nodes        Best solution   Best bound      Gap       Time
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      0       0                           -inf                0.023
      1       2                    5.83946e-07                0.146
      1       2  5.92848e-07   FP  5.83946e-07      0.00%     4.676

EXIT: Optimal solution found (assuming convexity).

Final Statistics for MIP
------------------------
Final objective value               =  5.92847868574609e-07
Final bound value                   =  5.83945507059980e-07
Final optimality gap (abs / rel)    =  8.90e-09 / 8.90e-09 (0.00%)
# of root cutting plane rounds      =  0
# of restarts                       =  0
# of nodes processed                =  1 (0.120s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  455 (0.444s)
# of gradient evaluations           =  374 (0.355s)
# of hessian evaluations            =  369 (0.595s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  8 (4.635s)
Total program time (secs)           =  4.680 (4.563 CPU time)
Time spent in evaluations (secs)    =  1.394

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 4.527s
Rounding heuristic                  =  0 / 0 / 0.001s
MPEC heuristic                      =  0 / 0 / 0.000s

===========================================================================

with:

"maxtime_real" => 1.0,

so almost 4.7secs of total time in KNITRO violating the 1sec limit.

If I had to guess, it’s because the code spends 4.5 seconds in the feasibility pump heuristic. So it doesn’t check time limit termination until after, and then it return with the optimal solution.

Can you send me a copy of the model? I’ll send you a private message with my email. To write the model to file, do:

JuMP.write_to_file(model, "bug_report.nl")

1 Like