From b9bfccf38dd7024534c1b1cccb2344180c5c3d66 Mon Sep 17 00:00:00 2001 From: Trevor McKay Date: Mon, 15 Sep 2025 10:56:37 -0400 Subject: [PATCH 1/3] Add optional timestamps enabled by env CUOPT_EXTRA_TIMESTAMPS --- cpp/src/linear_programming/cuopt_c.cpp | 39 +++++++++++++++++++ .../data_model/data_model.py | 5 +++ .../cuopt/cuopt/linear_programming/problem.py | 7 +++- .../cuopt/linear_programming/solver/solver.py | 13 ++++++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/cpp/src/linear_programming/cuopt_c.cpp b/cpp/src/linear_programming/cuopt_c.cpp index fc77e2323..651c62b89 100644 --- a/cpp/src/linear_programming/cuopt_c.cpp +++ b/cpp/src/linear_programming/cuopt_c.cpp @@ -27,8 +27,10 @@ #include #include +#include #include + using namespace cuopt::mps_parser; using namespace cuopt::linear_programming; @@ -74,6 +76,29 @@ cuopt_int_t cuOptGetVersion(cuopt_int_t* version_major, return CUOPT_SUCCESS; } +bool extraTimestamps() { + const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); + if (envValue == nullptr) { + return false; + } + + std::string value(envValue); + return (value == "True" || value == "true" || value == "1"); +} + + +double getCurrentTimestamp() { + auto now = std::chrono::system_clock::now(); + return std::chrono::duration(now.time_since_epoch()).count(); +} + +void printTimestamp(const std::string& label) { + if (extraTimestamps()) { + std::cout << std::fixed << std::setprecision(6); + std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; + } +} + cuopt_int_t cuOptReadProblem(const char* filename, cuOptOptimizationProblem* problem_ptr) { problem_and_stream_view_t* problem_and_stream = new problem_and_stream_view_t(); @@ -115,6 +140,9 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { + + printTimestamp("CUOPT_CREATE_PROBLEM"); + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_sense == nullptr || @@ -170,6 +198,9 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { + + printTimestamp("CUOPT_CREATE_PROBLEM"); + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_lower_bounds == nullptr || @@ -596,6 +627,8 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, cuOptSolverSettings settings, cuOptSolution* solution_ptr) { + printTimestamp("CUOPT_SOLVE_START"); + if (problem == nullptr) { return CUOPT_INVALID_ARGUMENT; } if (settings == nullptr) { return CUOPT_INVALID_ARGUMENT; } if (solution_ptr == nullptr) { return CUOPT_INVALID_ARGUMENT; } @@ -614,6 +647,9 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, solution_and_stream_view->mip_solution_ptr = new mip_solution_t( solve_mip(*op_problem, mip_settings)); *solution_ptr = static_cast(solution_and_stream_view); + + printTimestamp("CUOPT_SOLVE_RETURN"); + return static_cast( solution_and_stream_view->mip_solution_ptr->get_error_status().get_error_type()); } else { @@ -629,6 +665,9 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, new optimization_problem_solution_t( solve_lp(*op_problem, pdlp_settings)); *solution_ptr = static_cast(solution_and_stream_view); + + printTimestamp("CUOPT_SOLVE_RETURN"); + return static_cast( solution_and_stream_view->lp_solution_ptr->get_error_status().get_error_type()); } diff --git a/python/cuopt/cuopt/linear_programming/data_model/data_model.py b/python/cuopt/cuopt/linear_programming/data_model/data_model.py index 0301b3603..0d1c79ad1 100644 --- a/python/cuopt/cuopt/linear_programming/data_model/data_model.py +++ b/python/cuopt/cuopt/linear_programming/data_model/data_model.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import time +import os + from . import data_model_wrapper from .utilities import catch_cuopt_exception @@ -151,6 +154,8 @@ class DataModel(data_model_wrapper.DataModel): """ def __init__(self): + if os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in (True, "True", "true"): + print(f"CUOPT_CREATE_PROBLEM: {time.time()}") super().__init__() @catch_cuopt_exception diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 1a14e17cf..bd200b451 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -14,7 +14,7 @@ # limitations under the License. from enum import Enum - +import time import numpy as np import cuopt.linear_programming.data_model as data_model @@ -950,6 +950,10 @@ def solve(self, settings=solver_settings.SolverSettings()): >>> problem.solve() """ + # Move optional timing marker to the beginning of the + # routine + dm = data_model.DataModel() + # iterate through the constraints and construct the constraint matrix n = len(self.vars) self.row_pointers = [0] @@ -975,7 +979,6 @@ def solve(self, settings=solver_settings.SolverSettings()): self.upper_bound[j] = self.vars[j].getUpperBound() # Initialize datamodel - dm = data_model.DataModel() dm.set_csr_constraint_matrix( np.array(self.values), np.array(self.column_indicies), diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.py b/python/cuopt/cuopt/linear_programming/solver/solver.py index 12921ae7c..cd9e902d8 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.py +++ b/python/cuopt/cuopt/linear_programming/solver/solver.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import time +import os + from cuopt.linear_programming.solver import solver_wrapper from cuopt.linear_programming.solver_settings import SolverSettings from cuopt.utilities import catch_cuopt_exception @@ -78,6 +81,11 @@ def Solve(data_model, solver_settings=None): >>> # Print the value of one specific variable >>> print(solution.get_vars()["var_name"]) """ + + emit_stamps = os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in (True, "True", "true") + if emit_stamps: + print(f"CUOPT_SOLVE_START: {time.time()}") + if solver_settings is None: solver_settings = SolverSettings() @@ -95,11 +103,14 @@ def is_mip(var_types): # Mixed types - fallback to comprehensive check return any(vt == "I" or vt == b"I" for vt in var_types) - return solver_wrapper.Solve( + s = solver_wrapper.Solve( data_model, solver_settings, mip=is_mip(data_model.get_variable_types()), ) + if emit_stamps: + print(f"CUOPT_SOLVE_RETURN: {time.time()}") + return s @catch_cuopt_exception From 99c5ee9a653500e681b8b28c4d373dff30a2d9d8 Mon Sep 17 00:00:00 2001 From: Trevor McKay Date: Tue, 16 Sep 2025 15:27:39 -0400 Subject: [PATCH 2/3] move optional extra timestamp functions to utilities --- .../cuopt/utilities/timestamp_utils.hpp | 50 ++++++++++++++++ cpp/src/CMakeLists.txt | 3 +- cpp/src/linear_programming/cuopt_c.cpp | 34 ++--------- cpp/src/utilities/timestamp_utils.cpp | 58 +++++++++++++++++++ 4 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 cpp/include/cuopt/utilities/timestamp_utils.hpp create mode 100644 cpp/src/utilities/timestamp_utils.cpp diff --git a/cpp/include/cuopt/utilities/timestamp_utils.hpp b/cpp/include/cuopt/utilities/timestamp_utils.hpp new file mode 100644 index 000000000..ce7a96244 --- /dev/null +++ b/cpp/include/cuopt/utilities/timestamp_utils.hpp @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace cuopt { +namespace utilities { + +/** + * @brief Check if extra timestamps should be printed based on environment variable + * + * Checks the CUOPT_EXTRA_TIMESTAMPS environment variable once and caches the result. + * Returns true if the environment variable is set to "True", "true", or "1". + * + * @return true if extra timestamps are enabled, false otherwise + */ +bool extraTimestamps(); + +/** + * @brief Get current timestamp as seconds since epoch + * + * @return Current timestamp as a double representing seconds since epoch + */ +double getCurrentTimestamp(); + +/** + * @brief Print a timestamp with label if extra timestamps are enabled + * + * @param label The label to print with the timestamp + */ +void printTimestamp(const std::string& label); + +} // namespace utilities +} // namespace cuopt diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 76a742617..d6868b7d3 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -15,7 +15,8 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger_helper.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/linear_programming/cuopt_c.cpp b/cpp/src/linear_programming/cuopt_c.cpp index 651c62b89..b9c0334be 100644 --- a/cpp/src/linear_programming/cuopt_c.cpp +++ b/cpp/src/linear_programming/cuopt_c.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -76,29 +77,6 @@ cuopt_int_t cuOptGetVersion(cuopt_int_t* version_major, return CUOPT_SUCCESS; } -bool extraTimestamps() { - const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); - if (envValue == nullptr) { - return false; - } - - std::string value(envValue); - return (value == "True" || value == "true" || value == "1"); -} - - -double getCurrentTimestamp() { - auto now = std::chrono::system_clock::now(); - return std::chrono::duration(now.time_since_epoch()).count(); -} - -void printTimestamp(const std::string& label) { - if (extraTimestamps()) { - std::cout << std::fixed << std::setprecision(6); - std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; - } -} - cuopt_int_t cuOptReadProblem(const char* filename, cuOptOptimizationProblem* problem_ptr) { problem_and_stream_view_t* problem_and_stream = new problem_and_stream_view_t(); @@ -141,7 +119,7 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, cuOptOptimizationProblem* problem_ptr) { - printTimestamp("CUOPT_CREATE_PROBLEM"); + cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || @@ -199,7 +177,7 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, cuOptOptimizationProblem* problem_ptr) { - printTimestamp("CUOPT_CREATE_PROBLEM"); + cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || @@ -627,7 +605,7 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, cuOptSolverSettings settings, cuOptSolution* solution_ptr) { - printTimestamp("CUOPT_SOLVE_START"); + cuopt::utilities::printTimestamp("CUOPT_SOLVE_START"); if (problem == nullptr) { return CUOPT_INVALID_ARGUMENT; } if (settings == nullptr) { return CUOPT_INVALID_ARGUMENT; } @@ -648,7 +626,7 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, solve_mip(*op_problem, mip_settings)); *solution_ptr = static_cast(solution_and_stream_view); - printTimestamp("CUOPT_SOLVE_RETURN"); + cuopt::utilities::printTimestamp("CUOPT_SOLVE_RETURN"); return static_cast( solution_and_stream_view->mip_solution_ptr->get_error_status().get_error_type()); @@ -666,7 +644,7 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, solve_lp(*op_problem, pdlp_settings)); *solution_ptr = static_cast(solution_and_stream_view); - printTimestamp("CUOPT_SOLVE_RETURN"); + cuopt::utilities::printTimestamp("CUOPT_SOLVE_RETURN"); return static_cast( solution_and_stream_view->lp_solution_ptr->get_error_status().get_error_type()); diff --git a/cpp/src/utilities/timestamp_utils.cpp b/cpp/src/utilities/timestamp_utils.cpp new file mode 100644 index 000000000..d4fd82fec --- /dev/null +++ b/cpp/src/utilities/timestamp_utils.cpp @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +namespace cuopt { +namespace utilities { + +bool extraTimestamps() { + static bool initialized = false; + static bool enableTimestamps = false; + + if (!initialized) { + const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); + if (envValue != nullptr) { + std::string value(envValue); + enableTimestamps = (value == "True" || value == "true" || value == "1"); + } + initialized = true; + } + + return enableTimestamps; +} + +double getCurrentTimestamp() { + auto now = std::chrono::system_clock::now(); + return std::chrono::duration(now.time_since_epoch()).count(); +} + +void printTimestamp(const std::string& label) { + if (extraTimestamps()) { + std::cout << std::fixed << std::setprecision(6); + std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; + } +} + +} // namespace utilities +} // namespace cuopt From 92fa733a53a9637c85a67aad5b2fdb94fa76b709 Mon Sep 17 00:00:00 2001 From: Trevor McKay Date: Tue, 30 Sep 2025 11:37:37 -0400 Subject: [PATCH 3/3] check_style fixes --- .../cuopt/utilities/timestamp_utils.hpp | 8 ++-- cpp/src/linear_programming/cuopt_c.cpp | 11 ++--- cpp/src/utilities/timestamp_utils.cpp | 45 ++++++++++--------- .../data_model/data_model.py | 8 +++- .../cuopt/cuopt/linear_programming/problem.py | 4 +- .../cuopt/linear_programming/solver/solver.py | 8 +++- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/cpp/include/cuopt/utilities/timestamp_utils.hpp b/cpp/include/cuopt/utilities/timestamp_utils.hpp index ce7a96244..e539ca999 100644 --- a/cpp/include/cuopt/utilities/timestamp_utils.hpp +++ b/cpp/include/cuopt/utilities/timestamp_utils.hpp @@ -24,24 +24,24 @@ namespace utilities { /** * @brief Check if extra timestamps should be printed based on environment variable - * + * * Checks the CUOPT_EXTRA_TIMESTAMPS environment variable once and caches the result. * Returns true if the environment variable is set to "True", "true", or "1". - * + * * @return true if extra timestamps are enabled, false otherwise */ bool extraTimestamps(); /** * @brief Get current timestamp as seconds since epoch - * + * * @return Current timestamp as a double representing seconds since epoch */ double getCurrentTimestamp(); /** * @brief Print a timestamp with label if extra timestamps are enabled - * + * * @param label The label to print with the timestamp */ void printTimestamp(const std::string& label); diff --git a/cpp/src/linear_programming/cuopt_c.cpp b/cpp/src/linear_programming/cuopt_c.cpp index b9c0334be..df1fa1a4b 100644 --- a/cpp/src/linear_programming/cuopt_c.cpp +++ b/cpp/src/linear_programming/cuopt_c.cpp @@ -27,11 +27,10 @@ #include -#include #include +#include #include - using namespace cuopt::mps_parser; using namespace cuopt::linear_programming; @@ -118,9 +117,8 @@ cuopt_int_t cuOptCreateProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { - cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); - + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_sense == nullptr || @@ -176,9 +174,8 @@ cuopt_int_t cuOptCreateRangedProblem(cuopt_int_t num_constraints, const char* variable_types, cuOptOptimizationProblem* problem_ptr) { - cuopt::utilities::printTimestamp("CUOPT_CREATE_PROBLEM"); - + if (problem_ptr == nullptr || objective_coefficients == nullptr || constraint_matrix_row_offsets == nullptr || constraint_matrix_column_indices == nullptr || constraint_matrix_coefficent_values == nullptr || constraint_lower_bounds == nullptr || @@ -627,7 +624,7 @@ cuopt_int_t cuOptSolve(cuOptOptimizationProblem problem, *solution_ptr = static_cast(solution_and_stream_view); cuopt::utilities::printTimestamp("CUOPT_SOLVE_RETURN"); - + return static_cast( solution_and_stream_view->mip_solution_ptr->get_error_status().get_error_type()); } else { diff --git a/cpp/src/utilities/timestamp_utils.cpp b/cpp/src/utilities/timestamp_utils.cpp index d4fd82fec..e3dd1abee 100644 --- a/cpp/src/utilities/timestamp_utils.cpp +++ b/cpp/src/utilities/timestamp_utils.cpp @@ -26,32 +26,35 @@ namespace cuopt { namespace utilities { -bool extraTimestamps() { - static bool initialized = false; - static bool enableTimestamps = false; - - if (!initialized) { - const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); - if (envValue != nullptr) { - std::string value(envValue); - enableTimestamps = (value == "True" || value == "true" || value == "1"); - } - initialized = true; +bool extraTimestamps() +{ + static bool initialized = false; + static bool enableTimestamps = false; + + if (!initialized) { + const char* envValue = std::getenv("CUOPT_EXTRA_TIMESTAMPS"); + if (envValue != nullptr) { + std::string value(envValue); + enableTimestamps = (value == "True" || value == "true" || value == "1"); } - - return enableTimestamps; + initialized = true; + } + + return enableTimestamps; } -double getCurrentTimestamp() { - auto now = std::chrono::system_clock::now(); - return std::chrono::duration(now.time_since_epoch()).count(); +double getCurrentTimestamp() +{ + auto now = std::chrono::system_clock::now(); + return std::chrono::duration(now.time_since_epoch()).count(); } -void printTimestamp(const std::string& label) { - if (extraTimestamps()) { - std::cout << std::fixed << std::setprecision(6); - std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; - } +void printTimestamp(const std::string& label) +{ + if (extraTimestamps()) { + std::cout << std::fixed << std::setprecision(6); + std::cout << std::endl << label << ": " << getCurrentTimestamp() << std::endl; + } } } // namespace utilities diff --git a/python/cuopt/cuopt/linear_programming/data_model/data_model.py b/python/cuopt/cuopt/linear_programming/data_model/data_model.py index 26be06c5b..6279c3167 100644 --- a/python/cuopt/cuopt/linear_programming/data_model/data_model.py +++ b/python/cuopt/cuopt/linear_programming/data_model/data_model.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time import os +import time from . import data_model_wrapper from .utilities import catch_cuopt_exception @@ -154,7 +154,11 @@ class DataModel(data_model_wrapper.DataModel): """ def __init__(self): - if os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in (True, "True", "true"): + if os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in ( + True, + "True", + "true", + ): print(f"CUOPT_CREATE_PROBLEM: {time.time()}") super().__init__() diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 8d1220080..9baa965de 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -13,9 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. + import copy from enum import Enum -import time import cuopt_mps_parser import numpy as np @@ -769,7 +769,7 @@ def _from_data_model(self, dm): def _to_data_model(self): dm = data_model.DataModel() - + # iterate through the constraints and construct the constraint matrix n = len(self.vars) self.rhs = [] diff --git a/python/cuopt/cuopt/linear_programming/solver/solver.py b/python/cuopt/cuopt/linear_programming/solver/solver.py index cd9e902d8..61491c96f 100644 --- a/python/cuopt/cuopt/linear_programming/solver/solver.py +++ b/python/cuopt/cuopt/linear_programming/solver/solver.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time import os +import time from cuopt.linear_programming.solver import solver_wrapper from cuopt.linear_programming.solver_settings import SolverSettings @@ -82,7 +82,11 @@ def Solve(data_model, solver_settings=None): >>> print(solution.get_vars()["var_name"]) """ - emit_stamps = os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in (True, "True", "true") + emit_stamps = os.environ.get("CUOPT_EXTRA_TIMESTAMPS", False) in ( + True, + "True", + "true", + ) if emit_stamps: print(f"CUOPT_SOLVE_START: {time.time()}")