Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ To use cuOpt.jl, you must first separately install cuOpt.

**Installing cuOpt requires Linux.**

Note: This version of cuOpt.jl supports the Nvidia cuOpt 25.08, 25.10, and 25.12 releases.
Note: This version of cuOpt.jl supports the Nvidia cuOpt 26.02 releases.

Please refer to the [NVIDIA cuOpt documentation](https://docs.nvidia.com/cuopt/user-guide/latest/cuopt-c/quick-start.html#installation) for installation instructions.

Expand All @@ -48,7 +48,7 @@ Pkg.add("cuOpt")

To install cuOpt on [Google Colab](https://colab.research.google.com), do:
```julia
julia> cmd = run(`pip install --extra-index-url=https://pypi.nvidia.com libcuopt-cu12==25.12.\* nvidia-cuda-runtime-cu12==12.8.\*`);
julia> cmd = run(`pip install --extra-index-url=https://pypi.nvidia.com libcuopt-cu12==26.2.\* nvidia-cuda-runtime-cu12==12.8.\*`);
Comment thread
ramakrishnap-nv marked this conversation as resolved.

julia> push!(Base.DL_LOAD_PATH, "/usr/local/lib/python3.12/dist-packages/libcuopt/lib64")
```
Expand Down
2 changes: 1 addition & 1 deletion gen/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
Clang = "40e3b903-d033-50b4-a0cc-940c62c95e31"

[compat]
Clang = "0.17"
Clang = "0.19"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the most recent Clang version

3 changes: 2 additions & 1 deletion gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ export CUOPT_INCLUDE_PATH=/home/cuopt/.local/lib/python3.12/site-packages/libcuo
Run the following command to generate the C wrapper for cuOpt:

```bash
julia gen/gen.jl
julia --project=gen -e 'using Pkg; Pkg.instantiate()'
julia --project=gen gen/gen.jl
```
2 changes: 1 addition & 1 deletion src/cuOpt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function __init__()
error("Failed to get cuOpt library version (status code: $status)")
end
version = VersionNumber(major[], minor[], patch[])
min, max = v"25.08", v"25.13"
min, max = v"26.02", v"26.03"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively restricts to only v26.02

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so now a big question: is this a breaking change for users? How do they ensure they get the right combination of cuOpt and cuOpt.jl versions?

Can we document the compat matrix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose so, yes, given that cuOpt.jl code that worked yesterday with cuOpt v25.12 would error under this change. Note that this could occur because cuOpt is installed separately by the user, hence the cuOpt version is not guaranteed to be in sync with cuOpt.jl, i.e., the user may upgrade cuOpt.jl but forget to upgrade cuOpt.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document the compat matrix?

The README currently states supported cuOpt versions under installation instructions.
Would you prefer a table that lists cuOpt <--> cuOpt.jl versions?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @mlubin

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we tag this as 0.2, this seems fine to me.

if !(min <= version < max)
error(
"Incompatible cuOpt library version. Got $version, but supported versions are [$min, $max)",
Expand Down
206 changes: 206 additions & 0 deletions src/gen/libcuopt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ function cuOptReadProblem(filename, problem_ptr)
ccall((:cuOptReadProblem, libcuopt), cuopt_int_t, (Ptr{Cchar}, Ptr{cuOptOptimizationProblem}), filename, problem_ptr)
end

"""
cuOptWriteProblem(problem, filename, format)

Write an optimization problem to a file.

# Arguments
* `problem`:\\[in\\] - The optimization problem to write.
* `filename`:\\[in\\] - The path to the output file.
* `format`:\\[in\\] - The file format to use. Currently only [`CUOPT_FILE_FORMAT_MPS`](@ref) is supported.
# Returns
A status code indicating success or failure. Returns [`CUOPT_INVALID_ARGUMENT`](@ref) if an unsupported format is specified.
"""
function cuOptWriteProblem(problem, filename, format)
ccall((:cuOptWriteProblem, libcuopt), cuopt_int_t, (cuOptOptimizationProblem, Ptr{Cchar}, cuopt_int_t), problem, filename, format)
end

"""
cuOptCreateProblem(num_constraints, num_variables, objective_sense, objective_offset, objective_coefficients, constraint_matrix_row_offsets, constraint_matrix_column_indices, constraint_matrix_coefficent_values, constraint_sense, rhs, lower_bounds, upper_bounds, variable_types, problem_ptr)

Expand Down Expand Up @@ -581,6 +597,154 @@ function cuOptGetFloatParameter(settings, parameter_name, parameter_value)
ccall((:cuOptGetFloatParameter, libcuopt), cuopt_int_t, (cuOptSolverSettings, Ptr{Cchar}, Ptr{cuopt_float_t}), settings, parameter_name, parameter_value)
end

# typedef void ( * cuOptMIPGetSolutionCallback ) ( const cuopt_float_t * solution , const cuopt_float_t * objective_value , const cuopt_float_t * solution_bound , void * user_data )
"""
Type of callback for receiving incumbent MIP solutions with user context.

!!! note

All pointer arguments (solution, objective\\_value, solution\\_bound, user\\_data) refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns.

# Arguments
* `solution`:\\[in\\] - Pointer to incumbent solution values. The allocated array for solution pointer must be at least the number of variables in the original problem.
* `objective_value`:\\[in\\] - Pointer to incumbent objective value.
* `solution_bound`:\\[in\\] - Pointer to current solution (dual/user) bound.
* `user_data`:\\[in\\] - Pointer to user data.
"""
const cuOptMIPGetSolutionCallback = Ptr{Cvoid}

# typedef void ( * cuOptMIPSetSolutionCallback ) ( cuopt_float_t * solution , cuopt_float_t * objective_value , const cuopt_float_t * solution_bound , void * user_data )
"""
Type of callback for injecting MIP solutions with user context.

!!! note

All pointer arguments (solution, objective\\_value, solution\\_bound, user\\_data) refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns.

# Arguments
* `solution`:\\[out\\] - Pointer to solution values to set. The allocated array for solution pointer must be at least the number of variables in the original problem.
* `objective_value`:\\[out\\] - Pointer to objective value to set.
* `solution_bound`:\\[in\\] - Pointer to current solution (dual/user) bound.
* `user_data`:\\[in\\] - Pointer to user data.
"""
const cuOptMIPSetSolutionCallback = Ptr{Cvoid}

"""
cuOptSetMIPGetSolutionCallback(settings, callback, user_data)

Register a callback to receive incumbent MIP solutions.

!!! note

The callback arguments refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns.

# Arguments
* `settings`:\\[in\\] - The solver settings object.
* `callback`:\\[in\\] - Callback function to receive incumbent solutions.
* `user_data`:\\[in\\] - User-defined pointer passed through to the callback. It will be forwarded to `[`cuOptMIPGetSolutionCallback`](@ref)` when invoked.
# Returns
A status code indicating success or failure.
"""
function cuOptSetMIPGetSolutionCallback(settings, callback, user_data)
ccall((:cuOptSetMIPGetSolutionCallback, libcuopt), cuopt_int_t, (cuOptSolverSettings, cuOptMIPGetSolutionCallback, Ptr{Cvoid}), settings, callback, user_data)
end

"""
cuOptSetMIPSetSolutionCallback(settings, callback, user_data)

Register a callback to inject MIP solutions.

!!! note

Registering a set-solution callback disables presolve.

!!! note

The callback arguments refer to host memory and are only valid during the callback invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback returns.

# Arguments
* `settings`:\\[in\\] - The solver settings object.
* `callback`:\\[in\\] - Callback function to inject solutions.
* `user_data`:\\[in\\] - User-defined pointer passed through to the callback. It will be forwarded to `[`cuOptMIPSetSolutionCallback`](@ref)` when invoked.
# Returns
A status code indicating success or failure.
"""
function cuOptSetMIPSetSolutionCallback(settings, callback, user_data)
ccall((:cuOptSetMIPSetSolutionCallback, libcuopt), cuopt_int_t, (cuOptSolverSettings, cuOptMIPSetSolutionCallback, Ptr{Cvoid}), settings, callback, user_data)
end

"""
cuOptSetInitialPrimalSolution(settings, primal_solution, num_variables)

Set the initial primal solution for an LP solve.

!!! note

This function is only supported for PDLP.

!!! note

All pointer arguments (primal\\_solution) refer to host memory.

# Arguments
* `settings`:\\[in\\] - The solver settings object.
* `primal_solution`:\\[in\\] - A pointer to an array of type [`cuopt_float_t`](@ref) of size num\\_variables containing the initial primal values.
* `num_variables`:\\[in\\] - The number of variables (size of the primal\\_solution array).
# Returns
A status code indicating success or failure.
"""
function cuOptSetInitialPrimalSolution(settings, primal_solution, num_variables)
ccall((:cuOptSetInitialPrimalSolution, libcuopt), cuopt_int_t, (cuOptSolverSettings, Ptr{cuopt_float_t}, cuopt_int_t), settings, primal_solution, num_variables)
end

"""
cuOptSetInitialDualSolution(settings, dual_solution, num_constraints)

Set the initial dual solution for an LP solve.

!!! note

This function is only supported for PDLP.

!!! note

All pointer arguments (dual\\_solution) refer to host memory.

# Arguments
* `settings`:\\[in\\] - The solver settings object.
* `dual_solution`:\\[in\\] - A pointer to an array of type [`cuopt_float_t`](@ref) of size num\\_constraints containing the initial dual values.
* `num_constraints`:\\[in\\] - The number of constraints (size of the dual\\_solution array).
# Returns
A status code indicating success or failure.
"""
function cuOptSetInitialDualSolution(settings, dual_solution, num_constraints)
ccall((:cuOptSetInitialDualSolution, libcuopt), cuopt_int_t, (cuOptSolverSettings, Ptr{cuopt_float_t}, cuopt_int_t), settings, dual_solution, num_constraints)
end

"""
cuOptAddMIPStart(settings, solution, num_variables)

Add an initial solution (MIP start) for MIP solving.

This function can be called multiple times to add multiple MIP starts. The solver will use these as starting points for the MIP search.

\\attention Currently unsupported with presolve on.

!!! note

All pointer arguments (solution) refer to host memory.

# Arguments
* `settings`:\\[in\\] - The solver settings object.
* `solution`:\\[in\\] - A pointer to an array of type [`cuopt_float_t`](@ref) of size num\\_variables containing the solution values.
* `num_variables`:\\[in\\] - The number of variables (size of the solution array).
# Returns
A status code indicating success or failure.
"""
function cuOptAddMIPStart(settings, solution, num_variables)
ccall((:cuOptAddMIPStart, libcuopt), cuopt_int_t, (cuOptSolverSettings, Ptr{cuopt_float_t}, cuopt_int_t), settings, solution, num_variables)
end

"""
cuOptIsMIP(problem, is_mip_ptr)

Expand Down Expand Up @@ -788,6 +952,8 @@ const CUOPT_ITERATION_LIMIT = "iteration_limit"

const CUOPT_TIME_LIMIT = "time_limit"

const CUOPT_WORK_LIMIT = "work_limit"

const CUOPT_PDLP_SOLVER_MODE = "pdlp_solver_mode"

const CUOPT_METHOD = "method"
Expand Down Expand Up @@ -822,6 +988,8 @@ const CUOPT_PRESOLVE = "presolve"

const CUOPT_DUAL_POSTSOLVE = "dual_postsolve"

const CUOPT_MIP_DETERMINISM_MODE = "mip_determinism_mode"

const CUOPT_MIP_ABSOLUTE_TOLERANCE = "mip_absolute_tolerance"

const CUOPT_MIP_RELATIVE_TOLERANCE = "mip_relative_tolerance"
Expand All @@ -838,6 +1006,26 @@ const CUOPT_MIP_SCALING = "mip_scaling"

const CUOPT_MIP_PRESOLVE = "mip_presolve"

const CUOPT_MIP_RELIABILITY_BRANCHING = "mip_reliability_branching"

const CUOPT_MIP_CUT_PASSES = "mip_cut_passes"

const CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS = "mip_mixed_integer_rounding_cuts"

const CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS = "mip_mixed_integer_gomory_cuts"

const CUOPT_MIP_KNAPSACK_CUTS = "mip_knapsack_cuts"

const CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS = "mip_strong_chvatal_gomory_cuts"

const CUOPT_MIP_REDUCED_COST_STRENGTHENING = "mip_reduced_cost_strengthening"

const CUOPT_MIP_CUT_CHANGE_THRESHOLD = "mip_cut_change_threshold"

const CUOPT_MIP_CUT_MIN_ORTHOGONALITY = "mip_cut_min_orthogonality"

const CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING = "mip_batch_pdlp_strong_branching"

const CUOPT_SOLUTION_FILE = "solution_file"

const CUOPT_NUM_CPU_THREADS = "num_cpu_threads"
Expand All @@ -846,6 +1034,12 @@ const CUOPT_NUM_GPUS = "num_gpus"

const CUOPT_USER_PROBLEM_FILE = "user_problem_file"

const CUOPT_RANDOM_SEED = "random_seed"

const CUOPT_MODE_OPPORTUNISTIC = 0

const CUOPT_MODE_DETERMINISTIC = 1

const CUOPT_TERIMINATION_STATUS_NO_TERMINATION = 0

const CUOPT_TERIMINATION_STATUS_OPTIMAL = 1
Expand All @@ -866,6 +1060,8 @@ const CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND = 8

const CUOPT_TERIMINATION_STATUS_CONCURRENT_LIMIT = 9

const CUOPT_TERIMINATION_STATUS_WORK_LIMIT = 10

const CUOPT_MINIMIZE = 1

const CUOPT_MAXIMIZE = -1
Expand Down Expand Up @@ -900,6 +1096,8 @@ const CUOPT_METHOD_DUAL_SIMPLEX = 2

const CUOPT_METHOD_BARRIER = 3

const CUOPT_FILE_FORMAT_MPS = 0

const CUOPT_SUCCESS = 0

const CUOPT_INVALID_ARGUMENT = 1
Expand All @@ -913,3 +1111,11 @@ const CUOPT_VALIDATION_ERROR = 4
const CUOPT_OUT_OF_MEMORY = 5

const CUOPT_RUNTIME_ERROR = 6

const CUOPT_PRESOLVE_DEFAULT = -1

const CUOPT_PRESOLVE_OFF = 0

const CUOPT_PRESOLVE_PAPILO = 1

const CUOPT_PRESOLVE_PSLP = 2
10 changes: 6 additions & 4 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ function test_runtests_cache_optimizer()
],
);
exclude = [
# Upstream bug: https://github.com/NVIDIA/cuopt/issues/260
"test_constraint_ZeroOne_bounds_3",
# Upstream bug: https://github.com/NVIDIA/cuopt/issues/112
# upstream bug https://github.com/NVIDIA/cuopt/issues/923
"test_solve_TerminationStatus_DUAL_INFEASIBLE",
"test_linear_DUAL_INFEASIBLE",
"test_linear_DUAL_INFEASIBLE_2",
Comment on lines +59 to +62

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upstream bugs that caused previous exclusions were fixed, but I encountered a new one (see upstream issue) so had to exclude these.

],
)
return
Expand All @@ -73,7 +73,9 @@ function test_air05()
MOI.set(model, MOI.RawOptimizerAttribute(cuOpt.CUOPT_TIME_LIMIT), 60.0)
MOI.copy_to(model, src)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
# upstream bug: https://github.com/NVIDIA/cuopt/issues/855, should be fixed in 26.04
# @test_broken to be fixed when upgrading to cuopt 26.04
@test_broken MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstream bug is fixed on master, so @test_broken felt like the most appropriate

@test isapprox(MOI.get(model, MOI.ObjectiveValue()), 26374.0; rtol = 1e-4)
return
end
Expand Down
Loading