Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
37cc288
Add python API
Iroy30 Jul 22, 2025
11d78e4
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 22, 2025
e70c8a6
extract solution into variables and attributes
Iroy30 Jul 23, 2025
6f5f8f6
Merge remote-tracking branch 'iroy30/add_python_model_builder' into b…
Iroy30 Jul 23, 2025
a20d6b3
address review comments, iadd, isub
Iroy30 Jul 28, 2025
79c484c
address review, add tests
Iroy30 Jul 29, 2025
a904552
formatting
Iroy30 Jul 29, 2025
d3a4a8d
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 29, 2025
4877396
fix String Enum
Iroy30 Jul 29, 2025
543e9d7
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 29, 2025
115dbed
Merge remote-tracking branch 'iroy30/add_python_model_builder' into b…
Iroy30 Jul 29, 2025
bcd82b6
Update python/cuopt/cuopt/tests/linear_programming/test_python_API.py
tmckayus Jul 29, 2025
e503101
Update python/cuopt/cuopt/linear_programming/problem.py
tmckayus Jul 29, 2025
51ad041
Add docs and examples
rgsl888prabhu Jul 29, 2025
500f3bd
Merge branch 'add_python_model_builder' of github.com:Iroy30/NvidiaCu…
rgsl888prabhu Jul 29, 2025
a315272
update problem.py
Iroy30 Jul 30, 2025
9710591
update problem.py
Iroy30 Jul 30, 2025
13864b7
separate solver settings, add incumbent
Iroy30 Jul 30, 2025
efd0224
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 30, 2025
9df4079
update example
rgsl888prabhu Jul 30, 2025
ee3d0a5
Merge branch 'branch-25.08' into add_python_model_builder
rgsl888prabhu Jul 30, 2025
40abc69
update solver setting
rgsl888prabhu Jul 30, 2025
a5756eb
Merge branch 'add_python_model_builder' of github.com:Iroy30/NvidiaCu…
rgsl888prabhu Jul 30, 2025
67a3ff7
update cupy deps
rgsl888prabhu Jul 30, 2025
0e0d515
update doc
rgsl888prabhu Jul 30, 2025
330eb91
Add attributes and remove other non userfacing functions
rgsl888prabhu Jul 30, 2025
064c214
update docs and apply incumbent patch
rgsl888prabhu Jul 30, 2025
4172aeb
fix doc
rgsl888prabhu Jul 30, 2025
498987f
update tests, update isMIP in solver
Iroy30 Jul 30, 2025
c0d569f
merge
Iroy30 Jul 30, 2025
a27fb80
format
Iroy30 Jul 30, 2025
105e913
revert cuda-cupy changes and update attributes
Iroy30 Jul 30, 2025
7562bbe
dependencies.yaml update
Iroy30 Jul 30, 2025
d71b77e
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 30, 2025
f05c7b1
update tests, docs and address reviews
Iroy30 Jul 31, 2025
e8ae075
Merge remote-tracking branch 'iroy30/add_python_model_builder' into b…
Iroy30 Jul 31, 2025
8ae3746
Merge branch 'branch-25.08' into add_python_model_builder
Iroy30 Jul 31, 2025
e6ade53
Merge branch 'branch-25.08' into add_python_model_builder
rgsl888prabhu Jul 31, 2025
ae5e373
update docs
Iroy30 Jul 31, 2025
413e4e3
Merge remote-tracking branch 'iroy30/add_python_model_builder' into b…
Iroy30 Jul 31, 2025
2c0a1ab
more doc changes
Iroy30 Jul 31, 2025
41077fc
increase timelimit on incumbent test
Iroy30 Jul 31, 2025
f5c127d
formatting
Iroy30 Jul 31, 2025
a8a6515
update docs, tests and add back IsMIP
Iroy30 Jul 31, 2025
1967a3d
update incumbent example
Iroy30 Jul 31, 2025
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
1 change: 0 additions & 1 deletion dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,6 @@ dependencies:
- output_types: conda
packages:
- cupy>=12.0.0

depends_on_rapids_logger:
common:
- output_types: [conda, requirements, pyproject]
Expand Down
11 changes: 10 additions & 1 deletion docs/cuopt/source/cuopt-python/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,13 @@ This section contains details on the cuOpt Python package.
:name: Routing Optimization
:titlesonly:

Routing Optimization <routing/index.rst>
Routing Optimization <routing/index.rst>


.. toctree::
:maxdepth: 3
:caption: Linear Programming and Mixed Integer Linear Programming
:name: LP and MILP API
:titlesonly:

Linear Programming and Mixed Integer Linear Programming <lp-milp/index.rst>
14 changes: 14 additions & 0 deletions docs/cuopt/source/cuopt-python/lp-milp/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
=======================================================
Linear Programming and Mixed Integer Linear Programming
=======================================================

This section contains details on the cuOpt linear programming and mixed integer linear programming Python API.

.. toctree::
:maxdepth: 3
:caption: LP and MILP
:name: LP and MILP
:titlesonly:

lp-milp-api.rst
lp-milp-examples.rst
44 changes: 44 additions & 0 deletions docs/cuopt/source/cuopt-python/lp-milp/lp-milp-api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
=========================
LP and MILP API Reference
=========================

.. autoclass:: cuopt.linear_programming.problem.VType
:members:
:member-order: bysource
:undoc-members:
:exclude-members: capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill

.. autoclass:: cuopt.linear_programming.problem.CType
:members:
:member-order: bysource
:undoc-members:
:exclude-members: capitalize, casefold, center, count, encode, endswith, expandtabs, find, format, format_map, index, isalnum, isalpha, isascii, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper, join, ljust, lower, lstrip, maketrans, partition, removeprefix, removesuffix, replace, rfind, rindex, rjust, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, translate, upper, zfill

.. autoclass:: cuopt.linear_programming.problem.sense
:members:
:member-order: bysource
:exclude-members: __new__, __init__, _generate_next_value_, as_integer_ratio, bit_count, bit_length, conjugate, denominator, from_bytes, imag, is_integer, numerator, real, to_bytes
:no-inherited-members:

.. autoclass:: cuopt.linear_programming.problem.Problem
:members:
:undoc-members:
:show-inheritance:
:exclude-members: reset_solved_values, post_solve, dict_to_object, NumNZs, NumVariables, NumConstraints, IsMIP

.. autoclass:: cuopt.linear_programming.problem.Variable
:members:
:undoc-members:
:show-inheritance:
:exclude-members:

.. autoclass:: cuopt.linear_programming.problem.LinearExpression
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: cuopt.linear_programming.problem.Constraint
:members:
:undoc-members:
:show-inheritance:
:exclude-members: compute_slack
313 changes: 313 additions & 0 deletions docs/cuopt/source/cuopt-python/lp-milp/lp-milp-examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
====================
LP and MILP Examples
====================

This section contains examples of how to use the cuOpt linear programming and mixed integer linear programming Python API.

.. note::

The examples in this section are not exhaustive. They are provided to help you get started with the cuOpt linear programming and mixed integer linear programming Python API. For more examples, please refer to the `cuopt-examples GitHub repository <https://github.com/NVIDIA/cuopt-examples>`_.


Simple Linear Programming Example
---------------------------------

.. code-block:: python

from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

# Create a new problem
problem = Problem("Simple LP")

# Add variables
x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x")
y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y")

# Add constraints
problem.addConstraint(x + y <= 10, name="c1")
problem.addConstraint(x - y >= 0, name="c2")

# Set objective function
problem.setObjective(x + y, sense=MAXIMIZE)

# Configure solver settings
settings = SolverSettings()
settings.set_parameter("time_limit", 60)

# Solve the problem
problem.solve(settings)

# Check solution status
if problem.Status.name == "Optimal":
print(f"Optimal solution found in {problem.SolveTime:.2f} seconds")
print(f"x = {x.getValue()}")
print(f"y = {y.getValue()}")
print(f"Objective value = {problem.ObjValue}")

The response is as follows:

.. code-block:: text

Optimal solution found in 0.01 seconds
x = 10.0
y = 0.0
Objective value = 10.0

Mixed Integer Linear Programming Example
----------------------------------------

.. code-block:: python

from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

# Create a new MIP problem
problem = Problem("Simple MIP")

# Add integer variables with bounds
x = problem.addVariable(vtype=INTEGER, name="V_x")
y = problem.addVariable(lb=10, ub=50, vtype=INTEGER, name="V_y")

# Add constraints
problem.addConstraint(2 * x + 4 * y >= 230, name="C1")
problem.addConstraint(3 * x + 2 * y <= 190, name="C2")

# Set objective function
problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE)

# Configure solver settings
settings = SolverSettings()
settings.set_parameter("time_limit", 60)

# Solve the problem
problem.solve(settings)

# Check solution status and results
if problem.Status.name == "Optimal":
print(f"Optimal solution found in {problem.SolveTime:.2f} seconds")
print(f"x = {x.getValue()}")
print(f"y = {y.getValue()}")
print(f"Objective value = {problem.ObjValue}")
else:
print(f"Problem status: {problem.Status.name}")

The response is as follows:

.. code-block:: text

Optimal solution found in 0.00 seconds
x = 36.0
y = 40.99999999999999
Objective value = 303.0


Advanced Example: Production Planning
-------------------------------------

.. code-block:: python

from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

# Production planning problem
problem = Problem("Production Planning")

# Decision variables: production quantities
# x1 = units of product A
# x2 = units of product B
x1 = problem.addVariable(lb=10, vtype=INTEGER, name="Product_A")
x2 = problem.addVariable(lb=15, vtype=INTEGER, name="Product_B")

# Resource constraints
# Machine time: 2 hours per unit of A, 1 hour per unit of B, max 100 hours
problem.addConstraint(2 * x1 + x2 <= 100, name="Machine_Time")

# Labor: 1 hour per unit of A, 3 hours per unit of B, max 120 hours
problem.addConstraint(x1 + 3 * x2 <= 120, name="Labor_Hours")

# Material: 4 units per unit of A, 2 units per unit of B, max 200 units
problem.addConstraint(4 * x1 + 2 * x2 <= 200, name="Material")

# Objective: maximize profit
# Profit: $50 per unit of A, $30 per unit of B
problem.setObjective(50 * x1 + 30 * x2, sense=MAXIMIZE)

# Solve with time limit
settings = SolverSettings()
settings.set_parameter("time_limit", 30)
problem.solve(settings)

# Display results
if problem.Status.name == "Optimal":
print("=== Production Planning Solution ===")
print(f"Status: {problem.Status.name}")
print(f"Solve time: {problem.SolveTime:.2f} seconds")
print(f"Product A production: {x1.getValue()} units")
print(f"Product B production: {x2.getValue()} units")
print(f"Total profit: ${problem.ObjValue:.2f}")

else:
print(f"Problem not solved optimally. Status: {problem.Status.name}")

The response is as follows:

.. code-block:: text

=== Production Planning Solution ===

Status: Optimal
Solve time: 0.09 seconds
Product A production: 36.0 units
Product B production: 28.000000000000004 units
Total profit: $2640.00

Working with Expressions and Constraints
----------------------------------------

.. code-block:: python

from cuopt.linear_programming.problem import Problem, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings

problem = Problem("Expression Example")

# Create variables
x = problem.addVariable(lb=0, name="x")
y = problem.addVariable(lb=0, name="y")
z = problem.addVariable(lb=0, name="z")

# Create complex expressions
expr1 = 2 * x + 3 * y - z
expr2 = x + y + z

# Add constraints using expressions
problem.addConstraint(expr1 <= 100, name="Complex_Constraint_1")
problem.addConstraint(expr2 >= 20, name="Complex_Constraint_2")

# Add constraint with different senses
problem.addConstraint(x + y == 50, name="Equality_Constraint")
problem.addConstraint(1 * x <= 30, name="Upper_Bound_X")
problem.addConstraint(1 * y >= 10, name="Lower_Bound_Y")
problem.addConstraint(1 * z <= 100, name="Upper_Bound_Z")

# Set objective
problem.setObjective(x + 2 * y + 3 * z, sense=MAXIMIZE)

settings = SolverSettings()
settings.set_parameter("time_limit", 20)

problem.solve(settings)


if problem.Status.name == "Optimal":
print("=== Expression Example Results ===")
print(f"x = {x.getValue()}")
print(f"y = {y.getValue()}")
print(f"z = {z.getValue()}")
print(f"Objective value = {problem.ObjValue}")

The response is as follows:

.. code-block:: text

=== Expression Example Results ===
x = 0.0
y = 50.0
z = 99.99999999999999
Objective value = 399.99999999999994

Working with Incumbent Solutions
--------------------------------

Incumbent solutions are intermediate feasible solutions found during the MIP solving process. They represent the best integer-feasible solution discovered so far and can be accessed through callback functions.

.. note::
Incumbent solutions are only available for Mixed Integer Programming (MIP) problems, not for pure Linear Programming (LP) problems.

.. code-block:: python

from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE
from cuopt.linear_programming.solver_settings import SolverSettings
from cuopt.linear_programming.solver.solver_parameters import CUOPT_TIME_LIMIT
from cuopt.linear_programming.internals import GetSolutionCallback, SetSolutionCallback

# Create a callback class to receive incumbent solutions
class IncumbentCallback(GetSolutionCallback):
def __init__(self):
super().__init__()
self.solutions = []
self.n_callbacks = 0

def get_solution(self, solution, solution_cost):
"""
Called whenever the solver finds a new incumbent solution.

Parameters
----------
solution : array-like
The variable values of the incumbent solution
solution_cost : array-like
The objective value of the incumbent solution
"""
self.n_callbacks += 1

# Store the incumbent solution
incumbent = {
"solution": solution.copy_to_host(),
"cost": solution_cost.copy_to_host()[0],
"iteration": self.n_callbacks
}
self.solutions.append(incumbent)

print(f"Incumbent {self.n_callbacks}: {incumbent['solution']}, cost: {incumbent['cost']:.2f}")

# Create a more complex MIP problem that will generate multiple incumbents
problem = Problem("Incumbent Example")

# Add integer variables
x = problem.addVariable(vtype=INTEGER)
y = problem.addVariable(vtype=INTEGER)

# Add constraints to create a problem that will generate multiple incumbents
problem.addConstraint(2 * x + 4 * y >= 230)
problem.addConstraint(3 * x + 2 * y <= 190)

# Set objective to maximize
problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE)

# Configure solver settings with callback
settings = SolverSettings()
# Set the incumbent callback
incumbent_callback = IncumbentCallback()
settings.set_mip_callback(incumbent_callback)
settings.set_parameter(CUOPT_TIME_LIMIT, 30) # Allow enough time to find multiple incumbents

# Solve the problem
problem.solve(settings)

# Display final results
print(f"\n=== Final Results ===")
print(f"Problem status: {problem.Status.name}")
print(f"Solve time: {problem.SolveTime:.2f} seconds")
print(f"Final solution: x={x.getValue()}, y={y.getValue()}")
print(f"Final objective value: {problem.ObjValue:.2f}")

The response is as follows:

.. code-block:: text

Optimal solution found.
Incumbent 1: [ 0. 58.], cost: 174.00
Incumbent 2: [36. 41.], cost: 303.00
Generated fast solution in 0.158467 seconds with objective 303.000000
Consuming B&B solutions, solution queue size 2
Solution objective: 303.000000 , relative_mip_gap 0.000000 solution_bound 303.000000 presolve_time 0.043211 total_solve_time 0.160270 max constraint violation 0.000000 max int violation 0.000000 max var bounds violation 0.000000 nodes 4 simplex_iterations 3

=== Final Results ===
Problem status: Optimal
Solve time: 0.16 seconds
Final solution: x=36.0, y=40.99999999999999
Final objective value: 303.00


Loading