# Error with HiGHS independent parallel solves

**URL:** <https://discourse.julialang.org/t/error-with-highs-independent-parallel-solves/96048>\
**Category:** Optimization (Mathematical)\
**Tags:** question, jump\
**Created:** [March 14, 2023, 10:56am UTC](https://discourse.julialang.org/t/error-with-highs-independent-parallel-solves/96048 "2023-03-14T10:56:11Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Naunet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/naunet/32/47690_2.png) [@Naunet](https://discourse.julialang.org/u/Naunet)\
**Post date:** [March 14, 2023, 10:56am UTC](https://discourse.julialang.org/t/error-with-highs-independent-parallel-solves/96048/1 "2023-03-14T10:56:11Z")

</div>

A parallel example using HiGHS was recently published here : [Parallelism · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/algorithms/parallelism/#With-multi-threading)

However, when I change the model used to something that requires several iterations instead of just `presolve` it prints a bunch of `Iteration total error ${} + ${} + ${} + ${} = ${} != ${}` messages.

MWE : I’ve found this instance to be fairly reliable on my system, but potentially the seed may influence your results. Typically the larger `N` is, the more messages are printed.

> Julia Version 1.8.5  
> Commit 17cfb8e65ea (2023-01-08 06:45 UTC)  
> Platform Info:  
> OS: macOS (x86\_64-apple-darwin21.4.0)
> 
> HiGHS v1.5.0  
> JuMP v1.9.0

```julia
# Multi-threading the right way from https://jump.dev/JuMP.jl/stable/tutorials/algorithms/parallelism/#With-multi-threading
using JuMP
using HiGHS

solutions = Pair{Int,Float64}[]
my_lock = Threads.ReentrantLock()

# Copy of multi-objective knapsack problem from https://jump.dev/JuMP.jl/stable/tutorials/linear/multi_objective_knapsack/
# With changed input data and simplified objective to remove dependence on MultiObjectiveAlgorithms
function generate_model()
    profit = [79, 91, 79, 66, 72, 83, 65, 93, 59, 94, 82, 55, 74, 68, 57, 62, 62]
    desire = [99, 62, 95, 77, 55, 66, 77, 88, 59, 61, 86, 68, 50, 82, 51, 89, 56]
    weight = [85, 53, 72, 56, 75, 51, 74, 62, 61, 54, 93, 56, 52, 53, 66, 80, 64]
    capacity = 900
    N = length(profit)

    model = Model(HiGHS.Optimizer)
    @variable(model, x[1:N], Bin)
    @constraint(model, sum(weight[i] * x[i] for i in 1:N) <= capacity)
    @expression(model, profit_expr, sum(profit[i] * x[i] for i in 1:N))
    @expression(model, desire_expr, sum(desire[i] * x[i] for i in 1:N))
    @objective(model, Max, profit_expr + desire_expr)

    set_silent(model)
    optimize!(model)
    solution_summary(model)

    return argmax(i -> objective_value(model; result = i), 1:result_count(model))
end

Threads.@threads for i in 1:Threads.nthreads()
    @show Threads.threadid()

    res = generate_model()

    Threads.lock(my_lock) do
        push!(solutions, i => res)
    end
end

@show solutions

```

The only mention I can find of HiGHS being thread-safe is this docstring : [Docstrings · HiGHS\_jll.jl](https://docs.juliahub.com/HiGHS_jll/zNX2T/1.4.1+0/autodocs/#HiGHS_jll.highs-Tuple%7B%7D)

I guess the question is : is HiGHS thread-safe? Is this the intended behaviour? Is there are problem with the JuMP doc examples or my MWE?

---

<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:** [March 14, 2023, 9:33pm UTC](https://discourse.julialang.org/t/error-with-highs-independent-parallel-solves/96048/2 "2023-03-14T21:33:04Z")

</div>

Hi @Naunet, welcome to the forum!

This is an interesting find. I’ll admit, I didn’t test larger problems when I wrote the tutorial.

I can reproduce, and it looks like some part of HiGHS in fact isn’t thread-safe when it internally uses multiple threads. The quick fix is to limit HiGHS to one thread:

```julia
set_attribute(model, "threads", 1)

```

With that, I get:

```plaintext
julia> using JuMP

julia> import HiGHS

julia> solutions = Pair{Int,Float64}[]
Pair{Int64, Float64}[]

julia> my_lock = Threads.ReentrantLock()
ReentrantLock(nothing, Base.GenericCondition{Base.Threads.SpinLock}(Base.InvasiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), 0)

julia> function generate_model()
           profit = [79, 91, 79, 66, 72, 83, 65, 93, 59, 94, 82, 55, 74, 68, 57, 62, 62]
           desire = [99, 62, 95, 77, 55, 66, 77, 88, 59, 61, 86, 68, 50, 82, 51, 89, 56]
           weight = [85, 53, 72, 56, 75, 51, 74, 62, 61, 54, 93, 56, 52, 53, 66, 80, 64]
           capacity = 900
           N = length(profit)
           model = Model(HiGHS.Optimizer)
           set_attribute(model, "threads", 1)
           @variable(model, x[1:N], Bin)
           @constraint(model, sum(weight[i] * x[i] for i in 1:N) <= capacity)
           @expression(model, profit_expr, sum(profit[i] * x[i] for i in 1:N))
           @expression(model, desire_expr, sum(desire[i] * x[i] for i in 1:N))
           @objective(model, Max, profit_expr + desire_expr)
           set_silent(model)
           optimize!(model)
           solution_summary(model)
           return argmax(i -> objective_value(model; result = i), 1:result_count(model))
       end
generate_model (generic function with 1 method)

julia> Threads.@threads for i in 1:Threads.nthreads()
           res = generate_model()
           Threads.lock(my_lock) do
               push!(solutions, i => res)
           end
       end

julia> @show solutions
solutions = [4 => 1.0, 2 => 1.0, 3 => 1.0, 1 => 1.0]
4-element Vector{Pair{Int64, Float64}}:
 4 => 1.0
 2 => 1.0
 3 => 1.0
 1 => 1.0

```

I’ll update the tutorial with this fix, and I’ll talk to the HiGHS developers with your finding.
