From ee1f26bc6b9e30c516e7456ff3407d4685b048fc Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 17 Jul 2026 17:14:57 -0500 Subject: [PATCH 1/6] Make cuopt.routing importable without a GPU Importing cuopt.routing pulled in cuopt.routing.utils / utils_wrapper, whose generate_dataset had cudf.Series() as default argument values. Default arguments are evaluated at import time, and constructing an (even empty) cudf.Series requires a GPU, so `import cuopt.routing` failed with cudaErrorNoDevice on a GPU-less host -- e.g. a CPU-only client building and serializing a routing problem to submit to a remote solver. Move the empty-series construction from the signatures into the function bodies via None sentinels. `import cudf` itself does not touch the GPU, so the modules now import GPU-free; the helpers behave identically when called. Add __all__ to the routing package for explicit re-exports, and a subprocess test that imports the package with no visible GPU. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Ramakrishna Prabhu --- python/cuopt/cuopt/routing/__init__.py | 23 ++++++++++++++++++- python/cuopt/cuopt/routing/utils.py | 22 ++++++++++++++---- python/cuopt/cuopt/routing/utils_wrapper.pyx | 20 ++++++++++++---- .../tests/routing/test_gpu_free_import.py | 23 +++++++++++++++++++ 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 python/cuopt/cuopt/tests/routing/test_gpu_free_import.py diff --git a/python/cuopt/cuopt/routing/__init__.py b/python/cuopt/cuopt/routing/__init__.py index 081d58f998..be57929c71 100644 --- a/python/cuopt/cuopt/routing/__init__.py +++ b/python/cuopt/cuopt/routing/__init__.py @@ -9,5 +9,26 @@ update_routes_and_vehicles, ) from cuopt.routing.utils_wrapper import DatasetDistribution -from cuopt.routing.vehicle_routing import BatchSolve, DataModel, Solve, SolverSettings +from cuopt.routing.vehicle_routing import ( + BatchSolve, + DataModel, + Solve, + SolverSettings, +) from cuopt.routing.vehicle_routing_wrapper import ErrorStatus, Objective + +__all__ = [ + "Assignment", + "BatchSolve", + "DataModel", + "DatasetDistribution", + "ErrorStatus", + "Objective", + "SolutionStatus", + "Solve", + "SolverSettings", + "add_vehicle_constraints", + "create_pickup_delivery_data", + "generate_dataset", + "update_routes_and_vehicles", +] diff --git a/python/cuopt/cuopt/routing/utils.py b/python/cuopt/cuopt/routing/utils.py index 942e0018a6..0c5dc8a640 100644 --- a/python/cuopt/cuopt/routing/utils.py +++ b/python/cuopt/cuopt/routing/utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import glob @@ -17,10 +17,10 @@ def generate_dataset( locations=100, asymmetric=True, - min_demand=cudf.Series(), - max_demand=cudf.Series(), - min_capacities=cudf.Series(), - max_capacities=cudf.Series(), + min_demand=None, + max_demand=None, + min_capacities=None, + max_capacities=None, min_service_time=0, max_service_time=0, tw_tightness=0.0, @@ -114,6 +114,18 @@ def generate_dataset( Time windows and multi dimension capacity for each vehicle. """ + # Default to empty device series here rather than in the signature: + # a cudf.Series() default is constructed at import time and needs a GPU, + # which would make importing this module fail on a GPU-less host. + if min_demand is None: + min_demand = cudf.Series() + if max_demand is None: + max_demand = cudf.Series() + if min_capacities is None: + min_capacities = cudf.Series() + if max_capacities is None: + max_capacities = cudf.Series() + if ( min_demand.shape[0] != max_demand.shape[0] or max_demand.shape[0] != min_capacities.shape[0] diff --git a/python/cuopt/cuopt/routing/utils_wrapper.pyx b/python/cuopt/cuopt/routing/utils_wrapper.pyx index aa0800cf13..5879ac4798 100644 --- a/python/cuopt/cuopt/routing/utils_wrapper.pyx +++ b/python/cuopt/cuopt/routing/utils_wrapper.pyx @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # noqa +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -35,15 +35,27 @@ class DatasetDistribution(IntEnum): RANDOM_CLUSTERED = dataset_distribution_t.RANDOM_CLUSTERED -def generate_dataset(locations=100, asymmetric=True, min_demand=cudf.Series(), - max_demand=cudf.Series(), min_capacities=cudf.Series(), - max_capacities=cudf.Series(), min_service_time=0, +def generate_dataset(locations=100, asymmetric=True, min_demand=None, + max_demand=None, min_capacities=None, + max_capacities=None, min_service_time=0, max_service_time=0, tw_tightness=0.0, drop_return_trips=0.0, shifts=1, n_vehicle_types=1, n_matrix_types=1, distribution=DatasetDistribution.CLUSTERED, center_box=None, seed=0): + # Default to empty device series here rather than in the signature: + # a cudf.Series() default is constructed at import time and needs a GPU, + # which would make importing this module fail on a GPU-less host. + if min_demand is None: + min_demand = cudf.Series() + if max_demand is None: + max_demand = cudf.Series() + if min_capacities is None: + min_capacities = cudf.Series() + if max_capacities is None: + max_capacities = cudf.Series() + cdef unique_ptr[handle_t] handle_ptr handle_ptr.reset(new handle_t()) handle_ = handle_ptr.get() diff --git a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py new file mode 100644 index 0000000000..195f042ea0 --- /dev/null +++ b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess +import sys + + +def test_routing_imports_without_a_gpu(): + """cuopt.routing (and its dataset helpers) must import on a GPU-less host. + + The dataset helpers build empty cudf.Series objects; doing so in a default + argument would run at import time and fail with cudaErrorNoDevice where no + GPU is visible. This guards that the construction stays inside the function + bodies. Run in a subprocess with no visible GPU for a faithful check. + """ + code = ( + "import cuopt.routing as r\n" + "assert callable(r.generate_dataset)\n" + "assert r.DatasetDistribution.CLUSTERED is not None\n" + ) + env = {**os.environ, "CUDA_VISIBLE_DEVICES": ""} + subprocess.run([sys.executable, "-c", code], check=True, env=env) From be958c1ece01b2a276e35dda44a7e8d6064e0937 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 20 Jul 2026 11:41:52 -0500 Subject: [PATCH 2/6] Move dataset/batch helpers out of the cuopt.routing namespace The dataset-generation and batch helpers (generate_dataset, DatasetDistribution, add_vehicle_constraints, ...) are undocumented, local-testing utilities that require a GPU. Re-exporting them from the cuopt.routing package made `import cuopt.routing` pull in utils / utils_wrapper and fail with cudaErrorNoDevice on a GPU-less host -- e.g. a CPU-only client building and serializing a routing problem for a remote solver. Stop re-exporting them so importing routing no longer touches that GPU machinery; they remain available via `cuopt.routing.utils` for the tests that use them. save_data_model_to_yaml is imported from utils at its call site for the same reason. Repoint the one test that used the re-exports to cuopt.routing.utils, and add a subprocess test that (with no visible GPU) imports cuopt.routing, asserts utils/utils_wrapper are not pulled in, builds a DataModel, and imports cuopt.routing.utils. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Ramakrishna Prabhu --- python/cuopt/cuopt/routing/__init__.py | 18 +++----- python/cuopt/cuopt/routing/vehicle_routing.py | 7 ++- .../tests/routing/test_distance_engine.py | 6 +-- .../tests/routing/test_gpu_free_import.py | 45 ++++++++++++++----- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/python/cuopt/cuopt/routing/__init__.py b/python/cuopt/cuopt/routing/__init__.py index be57929c71..37c4c43b94 100644 --- a/python/cuopt/cuopt/routing/__init__.py +++ b/python/cuopt/cuopt/routing/__init__.py @@ -1,14 +1,13 @@ # SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +# Dataset-generation and batch helpers (generate_dataset, DatasetDistribution, +# add_vehicle_constraints, ...) are local-testing utilities that require a GPU +# to run. They deliberately live in cuopt.routing.utils and are NOT re-exported +# here, so importing cuopt.routing (e.g. to build/serialize a problem for a +# remote solver) does not pull in that GPU machinery. Import them from +# ``cuopt.routing.utils`` directly when needed. from cuopt.routing.assignment import Assignment, SolutionStatus -from cuopt.routing.utils import ( - add_vehicle_constraints, - create_pickup_delivery_data, - generate_dataset, - update_routes_and_vehicles, -) -from cuopt.routing.utils_wrapper import DatasetDistribution from cuopt.routing.vehicle_routing import ( BatchSolve, DataModel, @@ -21,14 +20,9 @@ "Assignment", "BatchSolve", "DataModel", - "DatasetDistribution", "ErrorStatus", "Objective", "SolutionStatus", "Solve", "SolverSettings", - "add_vehicle_constraints", - "create_pickup_delivery_data", - "generate_dataset", - "update_routes_and_vehicles", ] diff --git a/python/cuopt/cuopt/routing/vehicle_routing.py b/python/cuopt/cuopt/routing/vehicle_routing.py index 2a474df682..bff3aefc22 100644 --- a/python/cuopt/cuopt/routing/vehicle_routing.py +++ b/python/cuopt/cuopt/routing/vehicle_routing.py @@ -5,7 +5,6 @@ import cudf -from cuopt import routing from cuopt.routing import vehicle_routing_wrapper from cuopt.routing._deferred import _DeferredDataModel from cuopt.utilities import catch_cuopt_exception @@ -1561,7 +1560,11 @@ def Solve(data_model, solver_settings=None): data_model._build(), solver_settings ) if solver_settings.get_config_file_name() is not None: - routing.utils.save_data_model_to_yaml( + # imported here rather than at module scope: utils lives outside the + # cuopt.routing import path so that importing routing stays GPU-free. + from cuopt.routing import utils + + utils.save_data_model_to_yaml( data_model, solver_settings, solution, diff --git a/python/cuopt/cuopt/tests/routing/test_distance_engine.py b/python/cuopt/cuopt/tests/routing/test_distance_engine.py index d2cd4c199f..c027383be9 100644 --- a/python/cuopt/cuopt/tests/routing/test_distance_engine.py +++ b/python/cuopt/cuopt/tests/routing/test_distance_engine.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import numpy as np @@ -228,7 +228,7 @@ def test_compute_cost_matrix(): ] # Set vehicle constraints - vehicle_constraints = routing.add_vehicle_constraints( + vehicle_constraints = utils.add_vehicle_constraints( num_vehicles, vehicle_capacity, break_earliest, @@ -255,7 +255,7 @@ def test_compute_cost_matrix(): matrix_df, order_data, vehicle_data, - ) = routing.create_pickup_delivery_data( + ) = utils.create_pickup_delivery_data( matrix_pdf, single_day_order_pdf, depot, vehicle_constraints ) check_matrix( diff --git a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py index 195f042ea0..06f884cae0 100644 --- a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py +++ b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py @@ -6,18 +6,43 @@ import sys +def _run_without_gpu(code): + env = {**os.environ, "CUDA_VISIBLE_DEVICES": ""} + subprocess.run([sys.executable, "-c", code], check=True, env=env) + + def test_routing_imports_without_a_gpu(): - """cuopt.routing (and its dataset helpers) must import on a GPU-less host. + """cuopt.routing must import and build a model on a GPU-less host. - The dataset helpers build empty cudf.Series objects; doing so in a default - argument would run at import time and fail with cudaErrorNoDevice where no - GPU is visible. This guards that the construction stays inside the function - bodies. Run in a subprocess with no visible GPU for a faithful check. + The GPU dataset/batch helpers live in cuopt.routing.utils and are not + re-exported by the package, so importing cuopt.routing must not pull in + utils / utils_wrapper (which need a GPU). A CPU-only client can then build + and serialize a problem to submit to a remote solver. Run in a subprocess + with no visible GPU for a faithful check. """ - code = ( + _run_without_gpu( + "import sys\n" "import cuopt.routing as r\n" - "assert callable(r.generate_dataset)\n" - "assert r.DatasetDistribution.CLUSTERED is not None\n" + "assert 'cuopt.routing.utils' not in sys.modules\n" + "assert 'cuopt.routing.utils_wrapper' not in sys.modules\n" + # the GPU helpers are intentionally not part of the public surface + "assert not hasattr(r, 'generate_dataset')\n" + "import numpy as np\n" + "dm = r.DataModel(3, 2)\n" + "dm.add_cost_matrix(np.eye(3, dtype=np.float32))\n" + ) + + +def test_routing_utils_imports_without_a_gpu(): + """cuopt.routing.utils must import on a GPU-less host too. + + Its helpers build empty cudf.Series objects; doing so in a default argument + would run at import time and fail with cudaErrorNoDevice where no GPU is + visible. This guards that the construction stays in the function bodies, so + tests importing utils can be collected without a GPU. + """ + _run_without_gpu( + "from cuopt.routing import utils\n" + "assert callable(utils.generate_dataset)\n" + "assert callable(utils.add_vehicle_constraints)\n" ) - env = {**os.environ, "CUDA_VISIBLE_DEVICES": ""} - subprocess.run([sys.executable, "-c", code], check=True, env=env) From da4ab0891c645d55575bc9d90b9c43829b835430 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 20 Jul 2026 12:03:23 -0500 Subject: [PATCH 3/6] Drop cudf.Series default-arg change; namespace move alone is enough With the dataset/batch helpers no longer re-exported from cuopt.routing, importing the package never touches utils / utils_wrapper, so it is GPU-free without the None-sentinel change to generate_dataset. Revert utils.py and utils_wrapper.pyx to keep the diff focused on the namespace move, and drop the utils-imports-without-a-gpu test. The test that importing cuopt.routing does not require a GPU is kept. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Ramakrishna Prabhu --- python/cuopt/cuopt/routing/utils.py | 22 +++++-------------- python/cuopt/cuopt/routing/utils_wrapper.pyx | 20 ++++------------- .../tests/routing/test_gpu_free_import.py | 15 ------------- 3 files changed, 9 insertions(+), 48 deletions(-) diff --git a/python/cuopt/cuopt/routing/utils.py b/python/cuopt/cuopt/routing/utils.py index 0c5dc8a640..942e0018a6 100644 --- a/python/cuopt/cuopt/routing/utils.py +++ b/python/cuopt/cuopt/routing/utils.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import glob @@ -17,10 +17,10 @@ def generate_dataset( locations=100, asymmetric=True, - min_demand=None, - max_demand=None, - min_capacities=None, - max_capacities=None, + min_demand=cudf.Series(), + max_demand=cudf.Series(), + min_capacities=cudf.Series(), + max_capacities=cudf.Series(), min_service_time=0, max_service_time=0, tw_tightness=0.0, @@ -114,18 +114,6 @@ def generate_dataset( Time windows and multi dimension capacity for each vehicle. """ - # Default to empty device series here rather than in the signature: - # a cudf.Series() default is constructed at import time and needs a GPU, - # which would make importing this module fail on a GPU-less host. - if min_demand is None: - min_demand = cudf.Series() - if max_demand is None: - max_demand = cudf.Series() - if min_capacities is None: - min_capacities = cudf.Series() - if max_capacities is None: - max_capacities = cudf.Series() - if ( min_demand.shape[0] != max_demand.shape[0] or max_demand.shape[0] != min_capacities.shape[0] diff --git a/python/cuopt/cuopt/routing/utils_wrapper.pyx b/python/cuopt/cuopt/routing/utils_wrapper.pyx index 5879ac4798..aa0800cf13 100644 --- a/python/cuopt/cuopt/routing/utils_wrapper.pyx +++ b/python/cuopt/cuopt/routing/utils_wrapper.pyx @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # noqa # SPDX-License-Identifier: Apache-2.0 @@ -35,27 +35,15 @@ class DatasetDistribution(IntEnum): RANDOM_CLUSTERED = dataset_distribution_t.RANDOM_CLUSTERED -def generate_dataset(locations=100, asymmetric=True, min_demand=None, - max_demand=None, min_capacities=None, - max_capacities=None, min_service_time=0, +def generate_dataset(locations=100, asymmetric=True, min_demand=cudf.Series(), + max_demand=cudf.Series(), min_capacities=cudf.Series(), + max_capacities=cudf.Series(), min_service_time=0, max_service_time=0, tw_tightness=0.0, drop_return_trips=0.0, shifts=1, n_vehicle_types=1, n_matrix_types=1, distribution=DatasetDistribution.CLUSTERED, center_box=None, seed=0): - # Default to empty device series here rather than in the signature: - # a cudf.Series() default is constructed at import time and needs a GPU, - # which would make importing this module fail on a GPU-less host. - if min_demand is None: - min_demand = cudf.Series() - if max_demand is None: - max_demand = cudf.Series() - if min_capacities is None: - min_capacities = cudf.Series() - if max_capacities is None: - max_capacities = cudf.Series() - cdef unique_ptr[handle_t] handle_ptr handle_ptr.reset(new handle_t()) handle_ = handle_ptr.get() diff --git a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py index 06f884cae0..c936837ae8 100644 --- a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py +++ b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py @@ -31,18 +31,3 @@ def test_routing_imports_without_a_gpu(): "dm = r.DataModel(3, 2)\n" "dm.add_cost_matrix(np.eye(3, dtype=np.float32))\n" ) - - -def test_routing_utils_imports_without_a_gpu(): - """cuopt.routing.utils must import on a GPU-less host too. - - Its helpers build empty cudf.Series objects; doing so in a default argument - would run at import time and fail with cudaErrorNoDevice where no GPU is - visible. This guards that the construction stays in the function bodies, so - tests importing utils can be collected without a GPU. - """ - _run_without_gpu( - "from cuopt.routing import utils\n" - "assert callable(utils.generate_dataset)\n" - "assert callable(utils.add_vehicle_constraints)\n" - ) From ca9e0169487cfd363b6cf40d9413b1359f256434 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 20 Jul 2026 12:22:36 -0500 Subject: [PATCH 4/6] Guard the full set of names removed from the routing namespace The GPU-free-import test only asserted generate_dataset was gone, so a regression re-exposing DatasetDistribution, add_vehicle_constraints, create_pickup_delivery_data, or update_routes_and_vehicles would still pass. Enumerate all five moved names and fail if any leaks back onto cuopt.routing. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Ramakrishna Prabhu --- .../cuopt/tests/routing/test_gpu_free_import.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py index c936837ae8..ba7972e89b 100644 --- a/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py +++ b/python/cuopt/cuopt/tests/routing/test_gpu_free_import.py @@ -25,8 +25,17 @@ def test_routing_imports_without_a_gpu(): "import cuopt.routing as r\n" "assert 'cuopt.routing.utils' not in sys.modules\n" "assert 'cuopt.routing.utils_wrapper' not in sys.modules\n" - # the GPU helpers are intentionally not part of the public surface - "assert not hasattr(r, 'generate_dataset')\n" + # none of the GPU helpers are part of the public surface anymore; + # enumerate the full set so re-exposing any one of them fails the test + "moved = [\n" + " 'generate_dataset',\n" + " 'DatasetDistribution',\n" + " 'add_vehicle_constraints',\n" + " 'create_pickup_delivery_data',\n" + " 'update_routes_and_vehicles',\n" + "]\n" + "leaked = [n for n in moved if hasattr(r, n)]\n" + "assert not leaked, leaked\n" "import numpy as np\n" "dm = r.DataModel(3, 2)\n" "dm.add_cost_matrix(np.eye(3, dtype=np.float32))\n" From 06fc69e468e28e922de7e67f6b8c370d14890250 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 20 Jul 2026 15:10:58 -0500 Subject: [PATCH 5/6] test(mip): skip swath1 wheel tests crashing with bad_array_new_length Tracked in https://github.com/NVIDIA/cuopt/issues/1592. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Ramakrishna Prabhu --- .../linear_programming/test_incumbent_callbacks.py | 13 +++++++++++-- .../tests/linear_programming/test_lp_solver.py | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py index 65cddede95..42b9851674 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py @@ -110,10 +110,19 @@ def set_solution( ) +_SWATH1_BAD_ARRAY_SKIP = pytest.mark.skip( + reason=( + "Temporarily disabled: swath1 aborts with std::bad_array_new_length " + "in wheel builds after the fast MPS parser landed (#1429). " + "Tracked in https://github.com/NVIDIA/cuopt/issues/1592." + ) +) + + @pytest.mark.parametrize( "file_name", [ - ("/mip/swath1.mps"), + pytest.param("/mip/swath1.mps", marks=_SWATH1_BAD_ARRAY_SKIP), ("/mip/neos5-free-bound.mps"), ], ) @@ -124,7 +133,7 @@ def test_incumbent_get_callback(file_name): @pytest.mark.parametrize( "file_name", [ - ("/mip/swath1.mps"), + pytest.param("/mip/swath1.mps", marks=_SWATH1_BAD_ARRAY_SKIP), ("/mip/neos5-free-bound.mps"), ], ) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py index 7d9b611124..a303e1d602 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -676,6 +676,13 @@ def test_solved_by(): assert solution.get_solved_by() == SolverMethod.Barrier +@pytest.mark.skip( + reason=( + "Temporarily disabled: swath1 aborts with std::bad_array_new_length " + "in wheel builds after the fast MPS parser landed (#1429). " + "Tracked in https://github.com/NVIDIA/cuopt/issues/1592." + ) +) def test_heuristics_only(): file_path = RAPIDS_DATASET_ROOT_DIR + "/mip/swath1.mps" data_model_obj = Read(file_path) From c6fb1599183344b874fa0e926074b61c88e88c58 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 20 Jul 2026 15:58:27 -0500 Subject: [PATCH 6/6] test(mip): skip neos5-free-bound incumbent callback tests in wheel builds Also crashing with std::bad_array_new_length on all wheel runners in CI run 29775058848. Tracked in #1592. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Ramakrishna Prabhu --- .../linear_programming/test_incumbent_callbacks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py index 42b9851674..077f661b93 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_incumbent_callbacks.py @@ -110,9 +110,9 @@ def set_solution( ) -_SWATH1_BAD_ARRAY_SKIP = pytest.mark.skip( +_BAD_ARRAY_SKIP = pytest.mark.skip( reason=( - "Temporarily disabled: swath1 aborts with std::bad_array_new_length " + "Temporarily disabled: aborts with std::bad_array_new_length " "in wheel builds after the fast MPS parser landed (#1429). " "Tracked in https://github.com/NVIDIA/cuopt/issues/1592." ) @@ -122,8 +122,8 @@ def set_solution( @pytest.mark.parametrize( "file_name", [ - pytest.param("/mip/swath1.mps", marks=_SWATH1_BAD_ARRAY_SKIP), - ("/mip/neos5-free-bound.mps"), + pytest.param("/mip/swath1.mps", marks=_BAD_ARRAY_SKIP), + pytest.param("/mip/neos5-free-bound.mps", marks=_BAD_ARRAY_SKIP), ], ) def test_incumbent_get_callback(file_name): @@ -133,8 +133,8 @@ def test_incumbent_get_callback(file_name): @pytest.mark.parametrize( "file_name", [ - pytest.param("/mip/swath1.mps", marks=_SWATH1_BAD_ARRAY_SKIP), - ("/mip/neos5-free-bound.mps"), + pytest.param("/mip/swath1.mps", marks=_BAD_ARRAY_SKIP), + pytest.param("/mip/neos5-free-bound.mps", marks=_BAD_ARRAY_SKIP), ], ) def test_incumbent_get_set_callback(file_name):