# Non-convergent "weighted" objective function

**URL:** <https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785>\
**Category:** Optimization (Mathematical)\
**Tags:** gurobi, objective-function\
**Created:** [September 30, 2025, 11:27am UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785 "2025-09-30T11:27:16Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 11:27am UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/1 "2025-09-30T11:27:16Z")

</div>

I need to find a way to allocate a pool of resources to perform a series of daily services over the course of a month.  
There are obviously a long series of general (for all resources) and specific (for individual resources) constraints.  
The objective function to be minimized tries to make the assignment “balanced.”  
Using HIGHS, I can’t use quadratic functions, but I approximate the result by adding two auxiliary variables (lower\_bound=m and upper\_bound=M) and imposing Min(M-m).  
The script converges quickly and provides a result.

```julia-auto
...
model = Model(HiGHS.Optimizer)
#set_silent(model)
@variable(model, x[1:nr, 1:ng, turni], Bin)
@variable(model, z[1:nr, 1:ng], Bin)
@constraints(model, begin

    # each day must have exactly one Ti or one Fi
    fer1[k in union(t_feriali,t_card), g in g_feriali], sum(x[:,g,k]) == 1
    fer2[k in t_festivi, g in g_feriali], sum(x[:,g,k]) == 0

    fest1[k in t_festivi, g in g_festivi], sum(x[:,g,k]) == 1
    fest2[k in union(t_feriali, t_card), g in g_festivi], sum(x[:,g,k]) == 0

    sat[k in t_Cs, g in saturdays(mese,y)], sum(x[:,g,k]) == 1
    no_sat[k in ["CUMs","CLMs"], g in setdiff(1:ng,saturdays(mese,y))], sum(x[:,g,k]) == 0
...

    # no more than 5 turni consecutive  
    [r in 1:nr, g in 6:ng], sum(sum(x[r,i,k] for k in turni ) for i in g-5:g) <= 5

    # nothing can follow N or NF
     [r in 1:nr, g in 2:ng, k in turni], x[r,g,k] <= 1 - x[r,g-1,"NF"]
     [r in 1:nr, g in 2:ng, k in turni], x[r,g,k] <= 1 - x[r,g-1,"N"]

    #distance between N and N or NF >=4
     [r in 1:nr, g in 5:ng], sum(x[r,i,"N"]+x[r,i,"NF"] for i in g-4:g) <= 1
     
end)

peso = Dict{Int, Float64}()
for g in g_festivi
        peso[g] = 1.5
end
for g in g_feriali
        peso[g] = 1.0
end

# Carico di lavoro
@expression(model, workload[r=no_E], sum(x[r, g,k]*peso[g] for g in 1:ng, k in turni))

# Variabili per max/min carico
@variable(model, M) # massimo carico
@variable(model, m) # minimo carico

@constraint(model, [r in no_E], workload[r] <= M)
@constraint(model, [r in no_E], workload[r] >= m)

# Obiettivo: minimizzare squilibrio massimo
@objective(model, Min, M - m)
...

```

To obtain a more “fair” result, I tried solving the same problem by assigning different weights to some days of the month.  
In this form, I obtain an “infinite” sequence of the following lines:

 ![funzione obiettivo non converge](https://global.discourse-cdn.com/julialang/original/3X/3/3/33cbf926b458944d1757240e5b1aafa2f68b7f7c.png)

How can I read this output?  
Can you give me some general suggestions for overcoming this problem?  
If needed, I can post the complete script.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [September 30, 2025, 12:11pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/2 "2025-09-30T12:11:21Z")

</div>

An MIP formulation may become harder to solve if you modify the coefficient of the objective expression. In your case it seems that the solver is no longer making progress. And the Gap is so huge… I think you need to double check your formulation. For a problem in practice, how can you get a 60% gap…

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 1:10pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/3 "2025-09-30T13:10:51Z")

</div>

I have no idea what the GAP parameter means.  
I tried changing the weights to only integer values  
1 and 2 instead of 1 and 1.5, and it converged quickly.  
PS  
Is there a way to stop the calculation afterwards without aborting the terminal?  
I mean using some appropriate parameter in the optimize!() function call.

Or similarly at a higher level.

PPS  
It seems to not handle non-integer numbers.

I only have two sets of parameters.  
If I set s1=1 and s2=1.5, it doesn’t converge.  
If I set s1=2 and s2=3, it converges.

My implication is that the two formulations are mathematically equivalent.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [September 30, 2025, 1:58pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/4 "2025-09-30T13:58:49Z")

</div>

> [@rocco\_sprmnt21](#):
>
> Is there a way to stop the calculation afterwards without aborting the terminal?

`ctrl + c` is user’s interrupt, which is safe—you can re-call `optimize!` later.

> [@rocco\_sprmnt21](#):
>
> using some appropriate parameter

set some attributes before you `optimize!`, e.g. `TimeLimit`, `NodeLimit`, …

> [@rocco\_sprmnt21](#):
>
> I have no idea what the GAP parameter means.

You need to have some basic knowledge about global optimization, e.g. via reading books, articles, or tutorials…

> [@rocco\_sprmnt21](#):
>
> It seems to not handle non-integer numbers.

At least for Gurobi, I think its API only accepts data in `Float64` form, so they should have no difference. (I don’t know how HiGHS does)

* * *

Finally, I think you may consider revise your first post and give the _runnable_ code so others can understand your problem better.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 8:10pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/5 "2025-09-30T20:10:52Z")

</div>

Here’s the script; unfortunately, I have no way to simplify it.  
Lines 180-190 contain the ones that change the algorithm’s behavior.

> **Summary**
>
> ```julia-auto
> using XLSX, Dates , CSV, Tables, DataFrames, JuMP, HiGHS
> 
> function sol_jump(mese,y)
> 
> function getdata(path,sh,rngData)
> xf = XLSX.readxlsx(path)
> #sn=XLSX.sheetnames(xf)
> data=xf[sh][rngData]
> replace(data, missing=>".")
> end
> 
> function weekends(m,y)
> d=Date(Dates.Month(m), Dates.Year(y))
> #.(filter(d->dayofweek(d)==6||dayofweek(d)==7, d:lastdayofmonth(d)))
> day.(filter(d->Dates.issaturday(d)||Dates.issunday(d), d:lastdayofmonth(d)))
> end
> 
> function saturdays(m,y)
> d=Date(Dates.Month(m), Dates.Year(y))
> day.(filter(d->dayofweek(d)==6, d:lastdayofmonth(d)))
> end
> 
> function mondays(m,y)
> d=Date(Dates.Month(m), Dates.Year(y))
> day.(filter(d->dayofweek(d)==1, d:lastdayofmonth(d)))
> end
> 
> function MNDS(m,y)
> d=Date(Dates.Month(m), Dates.Year(y))
> starts=max(d,firstdayofweek(d))
> mondays=filter(d->dayofweek(d)==1, d:lastdayofmonth(d))
> ends=tonext.(d->dayofweek(d) == Dates.Friday, mondays)
> ends[end]=min(ends[end], lastdayofmonth(d))
> (:).(day.(mondays),day.(ends))
> end
> wks=MNDS(mese,y)
> #controllare se funziona per i diversi mesi.
> # se nel mese c'è prima un venerdì di un lunedì?
> # se lunedi+ 4 supera 31?
> # ecc
> 
> ops=["BAT",
> "CAVA",
> "CEL",  
> "DE_V",  
> "EMO",
> "GAT",
> "LAN",
> "LUO",  
> "MAL",
> "MAN",
> "PAG",
> "RIZ",
> "SOL"
> ]
> 
> BAT = findfirst(==( "BAT"), ops,) # 1
> CEL = findfirst(==( "CEL"), ops,) # 3
> DE_V = findfirst(==( "DE_V"), ops,) # 4
> EMO = findfirst(==( "EMO"), ops,) # 5
> GAT = findfirst(==( "GAT"), ops,) # 6
> LUO = findfirst(==( "LUO"), ops,) # 8
> MAN = findfirst(==( "MAN"), ops,) # 10
> PAG = findfirst(==( "PAG"), ops,) # 11
> 
> nr = length(ops)
> ops_lim=setdiff(1:nr,[1,3,4,5,8,11])
> no_E=setdiff(1:nr,EMO)
> ops_no_CUMs_CLMs=setdiff(1:nr,[4,8,11]) # tutti meno DE_V, LUO, PAG
> 
> ng = daysinmonth(Date(y,mese))
> 
> col='A'*('A'+ng-26)*"$(nr+2)" # colonna AE10 o AGF0
> H = (1,"B3:$col") ##### foglio excel,celle indispon. e ranges per persona
> res= getdata(path,H[1],H[2])
> 
> g_festivi = weekends(mese,y)
> g_feriali = setdiff(1:ng,g_festivi)
> 
> turni = ["M1","M2", "P", "N", "GF","NF","CUM1","CUM2","CLM","CUMs","CLMs"]
> t_feriali = turni[1:4]
> t_festivi = turni[5:6]
> t_card = turni[7:9]
> t_Cs = turni[10:11]
> t_mattina = ["M1","M2","CUM1","CUM2","CLM","GF","CUMs","CLMs"]
> 
> model = Model(HiGHS.Optimizer)
> #set_silent(model)
> @variable(model, x[1:nr, 1:ng, turni], Bin)
> @variable(model, z[1:nr, 1:ng], Bin)
> @constraints(model, begin
> 
> # each day must have exactly one t: (t in turni)
> fer1[k in union(t_feriali,t_card), g in g_feriali], sum(x[:,g,k]) == 1
> fer2[k in t_festivi, g in g_feriali], sum(x[:,g,k]) == 0
> 
> fest1[k in t_festivi, g in g_festivi], sum(x[:,g,k]) == 1
> fest2[k in union(t_feriali, t_card), g in g_festivi], sum(x[:,g,k]) == 0
> 
> sat[k in t_Cs, g in saturdays(mese,y)], sum(x[:,g,k]) == 1
> no_sat[k in ["CUMs","CLMs"], g in setdiff(1:ng,saturdays(mese,y))], sum(x[:,g,k]) == 0
> 
> # range of N and NF for row for month
> [r in ops_lim], sum(x[r,j,"N"]+x[r,j,"NF"] for j in 1:ng) <= 4 #4
> [r in ops_lim], sum(x[r,j,"N"]+x[r,j,"NF"] for j in 1:ng) >= 3 #3
>     
> [r in ops_lim], sum(x[r,j,"N"] for j in 1:ng) <= 3
> [r in ops_lim], sum(x[r,j,"N"] for j in 1:ng) >= 1
>    
> # max of NF for row for month
> [r in 1:nr], sum(x[r,j,"NF"] for j in g_festivi) <= 2 # 2
>     
> # NF==2 --> N<=1
> [r in ops_lim], sum(x[r,:,"NF"]) == sum(i * z[r, i] for i in 1:ng)
> [r in ops_lim], z[r,2] --> {sum(x[r, :, "N"]) <= 1}
> [r in ops_lim], sum(z[r,:]) <= 1
> 
> [r in 1:nr, w in wks], x[r,w[1],"CUM1"] --> {sum(x[r,Base.rest(w),"CUM1"])>=length(Base.rest(w))}
> [r in 1:nr, w in wks], x[r,w[1],"CUM2"] --> {sum(x[r,Base.rest(w),"CUM2"])>=length(Base.rest(w))}
> [r in 1:nr, w in wks], x[r,w[1],"CLM" ] --> {sum(x[r,Base.rest(w),"CLM"])>=length(Base.rest(w))}
> 
> # max turni nello stesso giorno per operatore
> # non notte insieme a mattina o pomeriggio, ecc.
> max_turni_fer_N_P[op in 1:nr, g in g_feriali], sum(x[op,g,k] for k in ["N","P"]) <= 1
> max_turni_fer_N_M[op in 1:nr, g in g_feriali], sum(x[op,g,k] for k in ["N";t_mattina]) <= 1
> max_turni_fer_P_M[op in 1:nr, g in g_feriali], sum(x[op,g,k] for k in ["P";t_mattina]) <= 1
> max_turni_fer[op in 1:nr, g in g_feriali], sum(x[op,g,k] for k in t_feriali) <= 2
> max_turni_CU[op in 1:nr, g in g_feriali], sum(x[op,g,k] for k in t_card) <= 1
> max_turni_fest[op in 1:nr, g in g_festivi], sum(x[op,g,k] for k in [t_festivi;t_Cs]) <= 1
> 
> # max num of GF and NF for row for weekends
> wk_lim[r in 1:nr], sum(x[r,j,"GF"]+x[r,j,"NF"] for j in weekends(mese,y)) <= 2 #2
>   
> # # range for month # sostituiti dalla funzione obiettivo
> # [r in no_E], sum(sum(x[r,j,k] for k in turni ) for j in 1:ng) <= 16 #16
> # [r in no_E], sum(sum(x[r,j,k] for k in turni ) for j in 1:ng) >= 14 #14
>     
> # vincoli sui turni specifici per operatore
> # es. BAT 3 turni di notte a mese
> # PAG, LUO, CEL MAN NON COPRONI I TURNI UTIC DI MATTINA
> # BAT SEMPRE IN UTIC LA MATTINA TRANNE SE FA LA NOTTE
> 
> sum(x[BAT,g,"N"] + x[BAT,g,"NF"] for g in 1:ng) == 3 # 3 notti
> sum(x[BAT,g,"P"] for g in 1:ng)== 0 # no pomeriggi
> sum(x[DE_V,g,k] for k in ["N", "NF"], g in 1:ng) == 0 # no notti
> sum(x[LUO,g, k] for k in ["N", "NF","M1","M2"], g in 1:ng) == 0 # no notti, no UTIC mattina
> 4 <= sum(x[LUO,g,"P"] for g in 1:ng) <= 5 # 4 o 5 pomeriggi
> sum(x[CEL,g,k] for k in ["GF", "NF","M1","M2"], g in 1:ng) == 0 # no festivi, no UTIC mattina
> 3 <= sum(x[CEL,g,"P"] for g in 1:ng) <= 4 # 3 o 4 pomeriggi
> sum(x[CEL,g,"N"] for g in 1:ng) ==2 # 2 notti
> sum(x[CEL,g,k] for k in ["M1","M2"], g in 1:ng) == 0 # no UTIC mattina
> sum(x[MAN,g,k] for k in ["M1","M2"], g in 1:ng) == 0 # no UTIC mattina
> 4 <= sum(x[PAG,g,"P"] for g in 1:ng) <= 5 # 4 o 5 pomeriggi
> sum(x[PAG,g,k] for k in ["N","GF","NF","M1","M2"] , g in 1:ng) == 0 # no notti, no festivi, no UTIC mattina
> sum(x[GAT,g,t] for g in 1:ng, t in t_card) == 0 # no cardio urgenza
> 
> # prevalentemente DE_V, LUO, PAG dovrebbero fare CU di sabato
> 
> sum(x[r,g,"CUMs"]+x[r,g,"CLMs"] for g in saturdays(mese,y),r in ops_no_CUMs_CLMs) <= 1
> 
> # no more than 5 turni consecutive  
> [r in 1:nr, g in 6:ng], sum(sum(x[r,i,k] for k in turni ) for i in g-5:g) <= 5
> 
> # nothing can follow N or NF
> [r in 1:nr, g in 2:ng, k in turni], x[r,g,k] <= 1 - x[r,g-1,"NF"]
> [r in 1:nr, g in 2:ng, k in turni], x[r,g,k] <= 1 - x[r,g-1,"N"]
> 
> #distance between N and N or NF >=4
> [r in 1:nr, g in 5:ng], sum(x[r,i,"N"]+x[r,i,"NF"] for i in g-4:g) <= 1
>      
> end)
> 
> peso = Dict{String, Int}() # THIS IS CONVERGENT
> for g in turni
> if g in [t_festivi; "N"]
> peso[g] = 2
> else
> peso[g] = 1
> end
> end
> 
> # peso = Dict{String, Float64}() THIS DOES NOT CONVERGE
> # for g in turni
> # if g in [t_festivi; "N"]
> # peso[g] = 1
> # else
> # peso[g] = 0.5
> # end
> # end
> 
> # Carico di lavoro
> @expression(model, workload[r=no_E], sum(x[r, g,k]*peso[k] for g in 1:ng, k in turni))
> 
> # Variabili per max/min carico
> @variable(model, M) # massimo carico
> @variable(model, m) # minimo carico
> 
> @constraint(model, [r in no_E], workload[r] <= M)
> @constraint(model, [r in no_E], workload[r] >= m)
> 
> # Obiettivo: minimizzare squilibrio massimo
> @objective(model, Min, M - m)
> 
> function init_model(res)
> fixm(i,j, k,b)= begin fix(x[i, j, k], b; force = true); res[i,j]=replace(res[i,j], r"[M|P|N|E]"=>".") end
> for i in 1:nr, j in 1:ng
> res[i, j] == "X" ? fixm.(i, j, turni, 0) :
> res[i, j] == "M" ? fixm.(i, j, t_mattina, 0) :
> # chiarire se res[i, j] == "P" esclude anche "GF" #
> res[i, j] == "P" ? fixm.(i, j, ["P"], 0) :
> res[i, j] == "N" ? fixm.(i, j, ["N","NF"], 0) :  
> res[i, j] == "MP" ? fixm.(i, j, ["P";t_mattina], 0) :
> res[i, j] == "PN" ? fixm.(i, j, ["P","N","NF"], 0) :
> res[i, j] == "MN" ? fixm.(i, j, ["N";t_mattina], 0) :
> res[i, j] == "E" ? fixm.(i, j, turni, [0,0,0,1,0,0,0,0,0,0,0]) :
> nothing
> end
> for i in 1:nr, j in 1:ng
> if (res[i,j]==".."||res[i,j]=="...") 
> res[i,j]="."
> end
> end
> end
> 
> init_model(res)
> optimize!(model)
> assert_is_solved_and_feasible(model)
> x,nr,ng,turni,ops,res
> end # soljump
> 
> function report_to_excel(path, x,nr,ng,turni,ops,res,shn="1")
> 
> for i in 1:nr, j in 1:ng, k in turni
> if value(x[i, j, k]) >= 0.5
> if res[i,j]=="."    
> res[i, j] = k
> else
> res[i, j] *= "+"*k
> end
> end
> end
> 
> vv=["" for i in 1:ng, j in 1:length(turni)]
> resp=DataFrame(vv,turni)
> for j in 1:ng, k in turni, i in 1:nr
> if value(x[i, j, k]) >= 0.5
> resp[j, k] *= ops[i]
> end
> end
> 
> giormi_it=Dict(
> "Wed" => "Mer",
> "Tue" => "Mar",
> "Thu" => "Gio",
> "Sun" => "Dom",
> "Mon" => "Lun",
> "Fri" => "Ven",
> "Sat" => "Sab")
> 
> git=[giormi_it[d] for d in Dates.format.(Date.(y, mese, 1:ng),"e")]
> gn = Dates.format.(Date.(y, mese, 1:ng),"dd")
> insertcols!(resp,1, :giorno => gn .* "-" .* git)
> 
> # output mode vista operatore
> XLSX.openxlsx(path, mode="rw") do xf
> sheet = XLSX.addsheet!(xf, "sol_jump_m_"*shn*"p")
> XLSX.writetable!(sheet, resp, anchor_cell=XLSX.CellRef("A1"))  
> end
> 
> # output mode vista servizio
> XLSX.openxlsx(path, mode="rw") do xf
> sheet = XLSX.addsheet!(xf, "sol_jump_"*shn)
> # dnames = Dates.format.(Date.(y, mese, 1:ng),"dd") .* "-" .* (giormi_it[d] for d in Dates.format.(Date.(y, mese, 1:ng),"e"))
> dnames = gn .* "-" .* git
> sol=DataFrame(res,dnames)
> insertcols!(sol,1, "giorno\\operatore" => ops)
> XLSX.writetable!(sheet, sol, anchor_cell=XLSX.CellRef("A1"))  
> end
> 
> # output mode vista sintesi/sommario
> XLSX.openxlsx(path, mode="rw") do xf
> sheet = XLSX.addsheet!(xf, "summary_jump_"*shn)
> vx=round.(Int,value.(x))
> df=DataFrame([t=>[sum(vx[j, :, t]) for j in 1:nr] for t in turni])
> transform!(df,Cols(:) =>ByRow((x...)->sum(x)) => "tot")
> transform!(df,["N","NF"]=>ByRow((x...)->sum(x)) => "tot_N")
> insertcols!(df,1, :operatore => ops)
> XLSX.writetable!(sheet, df, anchor_cell=XLSX.CellRef("A1"))  
> end
> 
> end
> 
> y, mese = 2025,10
> 
> path="C:\\Users\\sprmn\\OneDrive\\Desktop\\Turni San Giovanni\\test_ott_2025.xlsx"
> 
> x,nr,ng,turni,ops,res=sol_jump(mese,y)
> 
> report_to_excel(path, x,nr,ng,turni,ops,res,"peso_turni_1x2")
> 
> ```

Here is the Excel table that the script uses to load the boundary conditions

[test\_ott\_2025.xlsx](https://1drv.ms/x/c/7e478fc886cbb359/ESvQvEPjBRhHjx-NfGQc-tQBMSFyxYqO0HQQqIhIq69WZQ?e=bYHzHZ)

In the same Excel folder the script loads the processing results, in 3 separate sheets

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [September 30, 2025, 8:26pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/6 "2025-09-30T20:26:47Z")

</div>

Hi @rocco_sprmnt21,

There are three important columns to look at (you can ignore the rest):

1. BestSol: this is the objective value of the best integer solution HiGHS has found so far. Your current best objective is `0.5`.
2. BestBound: this is a lower bound on the optimal objective value. So far, HiGHS has proven that there cannot be a solution with a cost lower than `0.197`.
3. Gap: this is the relative difference between BestBound and BestSol. Currently, your gap is 60% = (BestSol - BestBound) / BestSol

HiGHS will stop searching when the Gap is small (\< 0.1%). You may also be happy with a larger gap, like 5%. You can change this by setting the [`mip_rel_gap`](https://ergo-code.github.io/HiGHS/stable/options/definitions/#mip_rel_gap) parameter in HiGHS.

For your model, HiGHS has reached a point where it is no longer finding improving `BestSol`. This should give reasonable confidence that `0.5` is very nearly an optimal solution. However, HiGHS is struggling to improve the BestBound to _prove_ that the BestSol is optimal.

If you don’t need a proof of optimality, you could interrupt the solve with CTRL+C and take the best solution. Or you could set a time limit (e.g., for 1 hr = 3600 sec, or even much shorter, like 60 sec).

If you do need a proof of optimality, you’ll need to improve your model, but this can be quite complicated if you don’t have a strong background in mixed-integer programming.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 8:29pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/7 "2025-09-30T20:29:23Z")

</div>

> [@odow](#):
>
> Or you could set a time limit (e.g., for 1 hr = 3600 sec, or even much shorter, like 60 sec).

how do you do this?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [September 30, 2025, 8:30pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/8 "2025-09-30T20:30:18Z")

</div>

`JuMP.set_time_limit_sec(model, 60)`

See [JuMP · JuMP](https://jump.dev/JuMP.jl/stable/api/JuMP/#set_time_limit_sec)

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 8:43pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/9 "2025-09-30T20:43:42Z")

</div>

This is what I get

```julia-auto
...
JuMP.set_time_limit_sec(model, 120)
...
# peso = Dict{String, Int}()
# for g in turni
# if g in [t_festivi; "N"]
# peso[g] = 2
# else
# peso[g] = 1
# end
# end

peso = Dict{String, Float64}()
for g in turni
     if g in [t_festivi; "N"]
        peso[g] = 1
     else
        peso[g] = 0.5
     end
end

...

Symmetry detection completed in 0.0s
Found 31 full orbitope(s) acting on 486 columns

      2962 215 99 0.00% 0.1383315577 0.4999999996 72.33% 1095 17 508 183172 59.5s
 B 3049 252 129 0.00% 0.1383315577 0.4999999995 72.33% 1119 18 571 186656 60.2s
      3195 262 193 0.00% 0.1383315577 0.4999999995 72.33% 1155 19 1082 201234 65.3s
 L 3444 323 290 0.00% 0.1383315577 0.4999993007 72.33% 1027 10 1206 208151 71.6s
      3682 424 344 0.00% 0.1383315577 0.4999993007 72.33% 930 29 1367 250458 77.6s
      3899 492 423 0.00% 0.1383315577 0.4999993007 72.33% 938 16 1531 267557 82.6s
      4266 570 572 0.00% 0.1383315577 0.4999993007 72.33% 947 24 1897 288779 91.2s
      4461 606 654 0.00% 0.1383315577 0.4999993007 72.33% 729 16 2057 303380 97.6s
      4688 697 710 0.01% 0.1383315577 0.4999993007 72.33% 734 34 2319 330637 106.3s

Restarting search from the root node
Model after restart has 1018 rows, 955 cols (953 bin., 0 int., 0 impl., 2 cont., 0 dom.fix.), and 10482 nonzeros

      4863 0 0 0.00% 0.1383315577 0.4999993007 72.33% 28 0 0 343593 111.0s
      4863 0 0 0.00% 0.1384673982 0.4999993007 72.31% 28 7 8 348774 111.3s

Symmetry detection completed in 0.0s
Found 31 full orbitope(s) acting on 486 columns

      5847 221 378 0.00% 0.1384673982 0.4999993007 72.31% 1727 13 402 375026 116.4s
      6606 403 669 0.00% 0.1384673982 0.4999993007 72.31% 1600 27 958 399779 120.0s

Solving report
  Status Time limit reached
  Primal bound 0.499999300654
  Dual bound 0.138467398201
  Gap 72.31% (tolerance: 0.01%)
  P-D integral 90.3413485309
  Solution status feasible
                    0.499999300654 (objective)
                    0 (bound viol.)
                    2.98272517796e-12 (int. viol.)
                    0 (row viol.)
  Timing 120.01 (total)
                    0.00 (presolve)
                    0.00 (solve)
                    0.00 (postsolve)
                    13934 (separation)
                    135771 (heuristics)
ERROR: The model was not solved correctly. Here is the output of `solution_summary` to help debug why this happened:

solution_summary(; result = 1, verbose = false)
├ solver_name : HiGHS
├ Termination
│ ├ termination_status : TIME_LIMIT
│ ├ result_count : 1
│ ├ raw_status : kHighsModelStatusTimeLimit
│ └ objective_bound : 1.38467e-01
├ Solution (result = 1)
│ ├ primal_status : FEASIBLE_POINT
│ ├ dual_status : NO_SOLUTION
│ ├ objective_value : 4.99999e-01
│ ├ dual_objective_value : NaN
│ └ relative_gap : 7.23065e-01
└ Work counters
  ├ solve_time (sec) : 1.20036e+02
  ├ simplex_iterations : 399779
  ├ barrier_iterations : -1
  └ node_count : 6606

Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] #assert_is_solved_and_feasible#111
   @ C:\Users\sprmn\.julia\packages\JuMP\N7h14\src\optimizer_interface.jl:1008 [inlined]
 [3] assert_is_solved_and_feasible
   @ C:\Users\sprmn\.julia\packages\JuMP\N7h14\src\optimizer_interface.jl:1002 [inlined]
 [4] sol_jump(mese::Int64, y::Int64)
   @ Main c:\Users\sprmn\.julia\envs\v1_10\J_turni_SG_MWE.jl:236
 [5] top-level scope
   @ c:\Users\sprmn\.julia\envs\v1_10\J_turni_SG_MWE.jl:311

```

Instead if I use the version with integer weights, I quickly get a solution.

I haven’t thought about it very carefully, but at first glance I’d say that, since everything is linear, the two problems with proportional weights are equivalent.  
So instead of weight1=1 and weight2=1.5,  
I’ll use weight1=2 and weight2=3, which seems more digestible for HIGHS.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [September 30, 2025, 8:57pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/10 "2025-09-30T20:57:59Z")

</div>

> [3] assert\_is\_solved\_and\_feasible

Yes, you can’t use this function because HiGHS did not “solve” it to optimality. You should check if `termination_status(model) == TIME_LIMIT` and `primal_status(model) == FEASIBLE_POINT`.

> but at first glance I’d say that, since everything is linear, the two problems with proportional weights are equivalent.

👍 Yes, keeping data integer is usually helpful.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 9:10pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/11 "2025-09-30T21:10:31Z")

</div>

> [@odow](#):
>
> Yes, you can’t use this function because HiGHS did not “solve” it to optimality. You should check if `termination_status(model) == TIME_LIMIT` and `primal_status(model) == FEASIBLE_POINT`.

With this line removed, here’s what I get.  
The output tables are different (but “close”) to those obtained with the integer weights and the script that completed but not due to the time limit reached.

```julia-auto

Solving report
  Status Time limit reached
  Primal bound 0.499999300654
  Dual bound 0.138467398201
  Gap 72.31% (tolerance: 0.01%)
  P-D integral 90.2967188094
  Solution status feasible
                    0.499999300654 (objective)
                    0 (bound viol.)
                    2.98272517796e-12 (int. viol.)
                    0 (row viol.)
  Timing 120.01 (total)
                    0.00 (presolve)
                    0.00 (solve)
                    0.00 (postsolve)
  Max sub-MIP depth 7
  Nodes 6717
  Repair LPs 0 (0 feasible; 0 iterations)
  LP iterations 401748 (total)
                    93517 (strong br.)
                    13941 (separation)
                    135771 (heuristics)
(3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:
    Dimension 1, Base.OneTo(13)
    Dimension 2, Base.OneTo(31)
    Dimension 3, ["M1", "M2", "P", "N", "GF", "NF", "CUM1", "CUM2", "CLM", "CUMs", "CLMs"]
And data, a 13×31×11 Array{VariableRef, 3}:
[:, :, "M1"] =
...

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [September 30, 2025, 9:21pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/12 "2025-09-30T21:21:21Z")

</div>

Sure. So the question now is: is that solution acceptable to you?

It’s probably good, but we can’t prove that there isn’t a solution that exists with a better objective value.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 9:22pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/13 "2025-09-30T21:22:28Z")

</div>

> [@odow](#):
>
> HiGHS will stop searching when the Gap is small (\< 0.1%). You may also be happy with a larger gap, like 5%. You can change this by setting the [`mip_rel_gap`](https://ergo-code.github.io/HiGHS/stable/options/definitions/#mip_rel_gap) parameter in HiGHS.

Very interesting. How do you change tolerance?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [September 30, 2025, 9:26pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/14 "2025-09-30T21:26:26Z")

</div>

`set_attribute(model, "mip_rel_gap", 0.05)` (for 5%)

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [September 30, 2025, 10:04pm UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/15 "2025-09-30T22:04:50Z")

</div>

In this case the tolerance criterion intervened before the time limit

```julia-auto

...

model = Model(HiGHS.Optimizer)
JuMP.set_time_limit_sec(model, 600)
set_attribute(model, "mip_rel_gap", 0.75)

...

Solving report
  Status Optimal
  Primal bound 0.499999999996
  Dual bound 0.125
  Gap 75% (tolerance: 75%)
  P-D integral 10.7619481432
  Solution status feasible
                    0.499999999996 (objective)
                    0 (bound viol.)
                    1.08695444656e-11 (int. viol.)
                    0 (row viol.)
  Timing 15.10 (total)
                    0.00 (presolve)
                    0.00 (solve)
                    0.00 (postsolve)
  Max sub-MIP depth 6
  Nodes 575
  Repair LPs 0 (0 feasible; 0 iterations)
  LP iterations 69878 (total)
                    485 (strong br.)
                    3060 (separation)
                    17260 (heuristics)
(3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:
    Dimension 1, Base.OneTo(13)
    Dimension 2, Base.OneTo(31)
    Dimension 3, ["M1", "M2", "P", "N", "GF", "NF", "CUM1", "CUM2", "CLM", "CUMs", "CLMs"]
And data, a 13×31×11 Array{VariableRef, 3}:
[:, :, "M1"] =
 x[1,1,M1] x[1,2,M1] x[1,3,M1] x[1,4,M1] x[1,5,M1] … x[1,27,M1] x[1,28,M1] x[1,29,M1] x[1,30,M1] x[1,31,M1]
 x[2,1,M1] x[2,2,M1] x[2,3,M1] x[2,4,M1] x[2,5,M1] x[2,27,M1] x[2,28,M1] x[2,29,M1] x[2,30,M1] x[2,31,M1]
 x[3,1,M1] x[3,2,M1] x[3,3,M1] x[3,4,M1] x[3,5,M1]     

```

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [October 1, 2025, 2:00am UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/16 "2025-10-01T02:00:40Z")

</div>

Yes, because you can see it terminates with `OPTIMAL` and `Gap = 75%`.

If I have one suggestion for you, is that you may at first focus on smaller scale systems (try a few toy examples) to build up your comprehension about the physical problem at hand, and the procedures of optimization.

I think currently you are involving too much staff  
`using XLSX, Dates , CSV, Tables, DataFrames, JuMP, HiGHS`  
that may distract you from focusing on more essential concepts. (e.g. the Gap, primal/dual bounds)

You can write a math program (e.g. using JuMP) from scratch at first. And after you fully understand your code, you may then add more decorations.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [October 1, 2025, 7:17am UTC](https://discourse.julialang.org/t/non-convergent-weighted-objective-function/132785/17 "2025-10-01T07:17:44Z")

</div>

> [@WalterMadelim](#):
>
> I think currently you are involving too much staff  
> `using XLSX, Dates , CSV, Tables, DataFrames, JuMP, HiGHS`  
> that may distract you from focusing on more essential concepts. (e.g. the Gap, primal/dual bounds)

Yes, the problem is indeed complex. I can’t simplify the boundary conditions or the set of constraints without distorting the problem.  
In this specific case, CSV and Tables don’t matter: they’re the remnants of previous attempts to address similar problems.  
Other ancillary functions for calculating certain sets of days could also be removed or better consolidated.  
But my goal, in this case, is purely practical. I need a solution, even an approximate one, that satisfies all the conditions mentioned.  
In general, it’s nice to have a good understanding of how things work, and thanks to @odow, I’ve learned some things that, besides being theoretically interesting, I believe will be concretely useful in similar situations.
